001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.model.cloud;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import javax.xml.bind.annotation.XmlAccessType;
023import javax.xml.bind.annotation.XmlAccessorType;
024import javax.xml.bind.annotation.XmlRootElement;
025import javax.xml.bind.annotation.XmlTransient;
026
027import org.apache.camel.CamelContext;
028import org.apache.camel.ExtendedCamelContext;
029import org.apache.camel.NoFactoryAvailableException;
030import org.apache.camel.cloud.ServiceLoadBalancer;
031import org.apache.camel.cloud.ServiceLoadBalancerFactory;
032import org.apache.camel.model.ProcessorDefinition;
033import org.apache.camel.spi.Metadata;
034import org.apache.camel.support.CamelContextHelper;
035import org.apache.camel.support.PropertyBindingSupport;
036import org.apache.camel.util.ObjectHelper;
037
038@Metadata(label = "routing,cloud,load-balancing")
039@XmlRootElement(name = "loadBalancerConfiguration")
040@XmlAccessorType(XmlAccessType.FIELD)
041public class ServiceCallServiceLoadBalancerConfiguration extends ServiceCallConfiguration implements ServiceLoadBalancerFactory {
042    @XmlTransient
043    private final ServiceCallDefinition parent;
044    @XmlTransient
045    private final String factoryKey;
046
047    public ServiceCallServiceLoadBalancerConfiguration() {
048        this(null, null);
049    }
050
051    public ServiceCallServiceLoadBalancerConfiguration(ServiceCallDefinition parent, String factoryKey) {
052        this.parent = parent;
053        this.factoryKey = factoryKey;
054    }
055
056    public ServiceCallDefinition end() {
057        return this.parent;
058    }
059
060    public ProcessorDefinition<?> endParent() {
061        return this.parent.end();
062    }
063
064    // *************************************************************************
065    //
066    // *************************************************************************
067
068    /**
069     * Adds a custom property to use.
070     * <p/>
071     * These properties are specific to what service call implementation are in
072     * use. For example if using ribbon, then the client properties are define
073     * in com.netflix.client.config.CommonClientConfigKey.
074     */
075    public ServiceCallServiceLoadBalancerConfiguration property(String key, String value) {
076        return (ServiceCallServiceLoadBalancerConfiguration) super.property(key, value);
077    }
078
079    // *************************************************************************
080    // Factory
081    // *************************************************************************
082
083    @Override
084    public ServiceLoadBalancer newInstance(CamelContext camelContext) throws Exception {
085        ObjectHelper.notNull(factoryKey, "LoadBalancer factoryKey");
086
087        ServiceLoadBalancer answer;
088
089        // First try to find the factory from the registry.
090        ServiceLoadBalancerFactory factory = CamelContextHelper.lookup(camelContext, factoryKey, ServiceLoadBalancerFactory.class);
091        if (factory != null) {
092            // If a factory is found in the registry do not re-configure it as
093            // it should be pre-configured.
094            answer = factory.newInstance(camelContext);
095        } else {
096
097            Class<?> type;
098            try {
099                // Then use Service factory.
100                type = camelContext.adapt(ExtendedCamelContext.class).getFactoryFinder(ServiceCallDefinitionConstants.RESOURCE_PATH).findClass(factoryKey).orElse(null);
101            } catch (Exception e) {
102                throw new NoFactoryAvailableException(ServiceCallDefinitionConstants.RESOURCE_PATH + factoryKey, e);
103            }
104
105            if (type != null) {
106                if (ServiceLoadBalancerFactory.class.isAssignableFrom(type)) {
107                    factory = (ServiceLoadBalancerFactory)camelContext.getInjector().newInstance(type, false);
108                } else {
109                    throw new IllegalArgumentException("Resolving LoadBalancer: " + factoryKey + " detected type conflict: Not a LoadBalancerFactory implementation. Found: "
110                                                       + type.getName());
111                }
112            }
113
114            try {
115                Map<String, Object> parameters = new HashMap<>();
116                camelContext.adapt(ExtendedCamelContext.class).getBeanIntrospection().getProperties(this, parameters, null, false);
117
118                parameters.replaceAll((k, v) -> {
119                    if (v instanceof String) {
120                        try {
121                            v = camelContext.resolvePropertyPlaceholders((String)v);
122                        } catch (Exception e) {
123                            throw new IllegalArgumentException(String.format("Exception while resolving %s (%s)", k, v.toString()), e);
124                        }
125                    }
126
127                    return v;
128                });
129
130                // Convert properties to Map<String, String>
131                parameters.put("properties", getPropertiesAsMap(camelContext));
132
133                postProcessFactoryParameters(camelContext, parameters);
134
135                PropertyBindingSupport.build().bind(camelContext, factory, parameters);
136
137                answer = factory.newInstance(camelContext);
138            } catch (Exception e) {
139                throw new IllegalArgumentException(e);
140            }
141        }
142
143        return answer;
144    }
145
146}