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;
021import java.util.Optional;
022
023import javax.xml.bind.annotation.XmlAccessType;
024import javax.xml.bind.annotation.XmlAccessorType;
025import javax.xml.bind.annotation.XmlRootElement;
026import javax.xml.bind.annotation.XmlTransient;
027
028import org.apache.camel.CamelContext;
029import org.apache.camel.ExtendedCamelContext;
030import org.apache.camel.NoFactoryAvailableException;
031import org.apache.camel.cloud.ServiceDiscovery;
032import org.apache.camel.cloud.ServiceDiscoveryFactory;
033import org.apache.camel.model.ProcessorDefinition;
034import org.apache.camel.spi.Metadata;
035import org.apache.camel.support.CamelContextHelper;
036import org.apache.camel.support.PropertyBindingSupport;
037import org.apache.camel.util.ObjectHelper;
038
039@Metadata(label = "routing,cloud,service-discovery")
040@XmlRootElement(name = "serviceDiscoveryConfiguration")
041@XmlAccessorType(XmlAccessType.FIELD)
042public class ServiceCallServiceDiscoveryConfiguration extends ServiceCallConfiguration implements ServiceDiscoveryFactory {
043    @XmlTransient
044    private final Optional<ServiceCallDefinition> parent;
045    @XmlTransient
046    private final String factoryKey;
047
048    public ServiceCallServiceDiscoveryConfiguration() {
049        this(null, null);
050    }
051
052    public ServiceCallServiceDiscoveryConfiguration(ServiceCallDefinition parent, String factoryKey) {
053        this.parent = Optional.ofNullable(parent);
054        this.factoryKey = factoryKey;
055    }
056
057    public ServiceCallDefinition end() {
058        return this.parent.orElseThrow(() -> new IllegalStateException("Parent definition is not set"));
059    }
060
061    public ProcessorDefinition<?> endParent() {
062        return this.parent.map(ServiceCallDefinition::end).orElseThrow(() -> new IllegalStateException("Parent definition is not set"));
063    }
064
065    // *************************************************************************
066    //
067    // *************************************************************************
068
069    public ServiceCallServiceDiscoveryConfiguration property(String key, String value) {
070        return (ServiceCallServiceDiscoveryConfiguration) super.property(key, value);
071    }
072
073    // *************************************************************************
074    // Factory
075    // *************************************************************************
076
077    @Override
078    public ServiceDiscovery newInstance(CamelContext camelContext) throws Exception {
079        ObjectHelper.notNull(factoryKey, "ServiceDiscovery factoryKey");
080
081        ServiceDiscovery answer;
082
083        // First try to find the factory from the registry.
084        ServiceDiscoveryFactory factory = CamelContextHelper.lookup(camelContext, factoryKey, ServiceDiscoveryFactory.class);
085        if (factory != null) {
086            // If a factory is found in the registry do not re-configure it as
087            // it should be pre-configured.
088            answer = factory.newInstance(camelContext);
089        } else {
090
091            Class<?> type;
092            try {
093                // Then use Service factory.
094                type = camelContext.adapt(ExtendedCamelContext.class).getFactoryFinder(ServiceCallDefinitionConstants.RESOURCE_PATH).findClass(factoryKey).orElse(null);
095            } catch (Exception e) {
096                throw new NoFactoryAvailableException(ServiceCallDefinitionConstants.RESOURCE_PATH + factoryKey, e);
097            }
098
099            if (type != null) {
100                if (ServiceDiscoveryFactory.class.isAssignableFrom(type)) {
101                    factory = (ServiceDiscoveryFactory)camelContext.getInjector().newInstance(type, false);
102                } else {
103                    throw new IllegalArgumentException("Resolving ServiceDiscovery: " + factoryKey
104                                                       + " detected type conflict: Not a ServiceDiscoveryFactory implementation. Found: " + type.getName());
105                }
106            }
107
108            try {
109                Map<String, Object> parameters = new HashMap<>();
110                camelContext.adapt(ExtendedCamelContext.class).getBeanIntrospection().getProperties(this, parameters, null, false);
111
112                parameters.replaceAll((k, v) -> {
113                    if (v instanceof String) {
114                        try {
115                            v = camelContext.resolvePropertyPlaceholders((String)v);
116                        } catch (Exception e) {
117                            throw new IllegalArgumentException(String.format("Exception while resolving %s (%s)", k, v.toString()), e);
118                        }
119                    }
120
121                    return v;
122                });
123
124                // Convert properties to Map<String, String>
125                parameters.put("properties", getPropertiesAsMap(camelContext));
126
127                postProcessFactoryParameters(camelContext, parameters);
128
129                PropertyBindingSupport.build().bind(camelContext, factory, parameters);
130
131                answer = factory.newInstance(camelContext);
132            } catch (Exception e) {
133                throw new IllegalArgumentException(e);
134            }
135        }
136
137        return answer;
138    }
139
140}