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 */
017
018package org.apache.camel.model.cloud;
019
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025import javax.xml.bind.annotation.XmlAccessType;
026import javax.xml.bind.annotation.XmlAccessorType;
027import javax.xml.bind.annotation.XmlElement;
028import javax.xml.bind.annotation.XmlRootElement;
029import javax.xml.bind.annotation.XmlTransient;
030
031import org.apache.camel.CamelContext;
032import org.apache.camel.NoFactoryAvailableException;
033import org.apache.camel.cloud.ServiceLoadBalancer;
034import org.apache.camel.cloud.ServiceLoadBalancerFactory;
035import org.apache.camel.model.IdentifiedType;
036import org.apache.camel.model.ProcessorDefinition;
037import org.apache.camel.model.PropertyDefinition;
038import org.apache.camel.spi.Metadata;
039import org.apache.camel.util.CamelContextHelper;
040import org.apache.camel.util.IntrospectionSupport;
041import org.apache.camel.util.ObjectHelper;
042
043@Metadata(label = "routing,cloud,load-balancing")
044@XmlRootElement(name = "loadBalancerConfiguration")
045@XmlAccessorType(XmlAccessType.FIELD)
046public class ServiceCallServiceLoadBalancerConfiguration extends IdentifiedType implements ServiceLoadBalancerFactory {
047    @XmlTransient
048    private final ServiceCallDefinition parent;
049    @XmlTransient
050    private final String factoryKey;
051    @XmlElement(name = "properties") @Metadata(label = "advanced")
052    private List<PropertyDefinition> properties;
053
054    public ServiceCallServiceLoadBalancerConfiguration() {
055        this(null, null);
056    }
057
058    public ServiceCallServiceLoadBalancerConfiguration(ServiceCallDefinition parent, String factoryKey) {
059        this.parent = parent;
060        this.factoryKey = factoryKey;
061    }
062
063    public ServiceCallDefinition end() {
064        return this.parent;
065    }
066
067    public ProcessorDefinition<?> endParent() {
068        return this.parent.end();
069    }
070
071    // *************************************************************************
072    //
073    // *************************************************************************
074
075    public List<PropertyDefinition> getProperties() {
076        return properties;
077    }
078
079    /**
080     * Set client properties to use.
081     * <p/>
082     * These properties are specific to what service call implementation are in
083     * use. For example if using ribbon, then the client properties are define
084     * in com.netflix.client.config.CommonClientConfigKey.
085     */
086    public void setProperties(List<PropertyDefinition> properties) {
087        this.properties = properties;
088    }
089
090    /**
091     * Adds a custom property to use.
092     * <p/>
093     * These properties are specific to what service call implementation are in
094     * use. For example if using ribbon, then the client properties are define
095     * in com.netflix.client.config.CommonClientConfigKey.
096     */
097    public ServiceCallServiceLoadBalancerConfiguration property(String key, String value) {
098        if (properties == null) {
099            properties = new ArrayList<>();
100        }
101        PropertyDefinition prop = new PropertyDefinition();
102        prop.setKey(key);
103        prop.setValue(value);
104        properties.add(prop);
105        return this;
106    }
107
108    protected Map<String, String> getPropertiesAsMap(CamelContext camelContext) throws Exception {
109        Map<String, String> answer;
110
111        if (properties == null || properties.isEmpty()) {
112            answer = Collections.emptyMap();
113        } else {
114            answer = new HashMap<>();
115            for (PropertyDefinition prop : properties) {
116                // support property placeholders
117                String key = CamelContextHelper.parseText(camelContext, prop.getKey());
118                String value = CamelContextHelper.parseText(camelContext, prop.getValue());
119                answer.put(key, value);
120            }
121        }
122
123        return answer;
124    }
125
126    // *************************************************************************
127    // Factory
128    // *************************************************************************
129
130    @Override
131    public ServiceLoadBalancer newInstance(CamelContext camelContext) throws Exception {
132        ObjectHelper.notNull(factoryKey, "LoadBalancer factoryKey");
133
134        ServiceLoadBalancer answer;
135
136        // First try to find the factory from the registry.
137        ServiceLoadBalancerFactory factory = CamelContextHelper.lookup(camelContext, factoryKey, ServiceLoadBalancerFactory.class);
138        if (factory != null) {
139            // If a factory is found in the registry do not re-configure it as
140            // it should be pre-configured.
141            answer = factory.newInstance(camelContext);
142        } else {
143
144            Class<?> type;
145            try {
146                // Then use Service factory.
147                type = camelContext.getFactoryFinder(ServiceCallDefinitionConstants.RESOURCE_PATH).findClass(factoryKey);
148            } catch (Exception e) {
149                throw new NoFactoryAvailableException(ServiceCallDefinitionConstants.RESOURCE_PATH + factoryKey, e);
150            }
151
152            if (type != null) {
153                if (ServiceLoadBalancerFactory.class.isAssignableFrom(type)) {
154                    factory = (ServiceLoadBalancerFactory) camelContext.getInjector().newInstance(type);
155                } else {
156                    throw new IllegalArgumentException(
157                        "Resolving LoadBalancer: " + factoryKey + " detected type conflict: Not a LoadBalancerFactory implementation. Found: " + type.getName());
158                }
159            }
160
161            try {
162                Map<String, Object> parameters = new HashMap<>();
163                IntrospectionSupport.getProperties(this, parameters, null, false);
164
165                parameters.replaceAll(
166                    (k, v) -> {
167                        if (v instanceof String) {
168                            try {
169                                v = camelContext.resolvePropertyPlaceholders((String) v);
170                            } catch (Exception e) {
171                                throw new IllegalArgumentException(
172                                    String.format("Exception while resolving %s (%s)", k, v.toString()),
173                                    e
174                                );
175                            }
176                        }
177
178                        return v;
179                    }
180                );
181
182                // Convert properties to Map<String, String>
183                parameters.put("properties", getPropertiesAsMap(camelContext));
184
185                postProcessFactoryParameters(camelContext, parameters);
186
187                IntrospectionSupport.setProperties(factory, parameters);
188
189                answer = factory.newInstance(camelContext);
190            } catch (Exception e) {
191                throw new IllegalArgumentException(e);
192            }
193        }
194
195        return answer;
196    }
197
198    // *************************************************************************
199    // Utilities
200    // *************************************************************************
201
202    protected void postProcessFactoryParameters(CamelContext camelContext, Map<String, Object> parameters) throws Exception  {
203    }
204}