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.XmlAttribute;
025import javax.xml.bind.annotation.XmlElementRef;
026import javax.xml.bind.annotation.XmlRootElement;
027import javax.xml.bind.annotation.XmlTransient;
028
029import org.apache.camel.CamelContext;
030import org.apache.camel.Expression;
031import org.apache.camel.ExtendedCamelContext;
032import org.apache.camel.NoFactoryAvailableException;
033import org.apache.camel.cloud.ServiceCallConstants;
034import org.apache.camel.cloud.ServiceExpressionFactory;
035import org.apache.camel.model.ProcessorDefinition;
036import org.apache.camel.model.language.ExpressionDefinition;
037import org.apache.camel.spi.Metadata;
038import org.apache.camel.support.CamelContextHelper;
039import org.apache.camel.support.PropertyBindingSupport;
040
041@Metadata(label = "routing,cloud")
042@XmlRootElement(name = "serviceExpression")
043@XmlAccessorType(XmlAccessType.FIELD)
044public class ServiceCallExpressionConfiguration extends ServiceCallConfiguration implements ServiceExpressionFactory {
045    @XmlTransient
046    private final ServiceCallDefinition parent;
047    @XmlTransient
048    private final String factoryKey;
049    @XmlAttribute
050    @Metadata(defaultValue = ServiceCallConstants.SERVICE_HOST)
051    private String hostHeader = ServiceCallConstants.SERVICE_HOST;
052    @XmlAttribute
053    @Metadata(defaultValue = ServiceCallConstants.SERVICE_PORT)
054    private String portHeader = ServiceCallConstants.SERVICE_PORT;
055    @XmlElementRef(required = false)
056    private ExpressionDefinition expressionType;
057    @XmlTransient
058    private Expression expression;
059
060    public ServiceCallExpressionConfiguration() {
061        this(null, null);
062    }
063
064    public ServiceCallExpressionConfiguration(ServiceCallDefinition parent, String factoryKey) {
065        this.parent = parent;
066        this.factoryKey = factoryKey;
067    }
068
069    public ServiceCallDefinition end() {
070        return this.parent;
071    }
072
073    public ProcessorDefinition<?> endParent() {
074        return this.parent.end();
075    }
076
077    // *************************************************************************
078    //
079    // *************************************************************************
080
081    public ServiceCallServiceChooserConfiguration property(String key, String value) {
082        return (ServiceCallServiceChooserConfiguration) super.property(key, value);
083    }
084
085    public String getHostHeader() {
086        return hostHeader;
087    }
088
089    /**
090     * The header that holds the service host information, default
091     * ServiceCallConstants.SERVICE_HOST
092     */
093    public void setHostHeader(String hostHeader) {
094        this.hostHeader = hostHeader;
095    }
096
097    public String getPortHeader() {
098        return portHeader;
099    }
100
101    /**
102     * The header that holds the service port information, default
103     * ServiceCallConstants.SERVICE_PORT
104     */
105    public void setPortHeader(String portHeader) {
106        this.portHeader = portHeader;
107    }
108
109    public ExpressionDefinition getExpressionType() {
110        return expressionType;
111    }
112
113    public void setExpressionType(ExpressionDefinition expressionType) {
114        this.expressionType = expressionType;
115    }
116
117    public Expression getExpression() {
118        return expression;
119    }
120
121    public void setExpression(Expression expression) {
122        this.expression = expression;
123    }
124
125    /**
126     * The header that holds the service host information, default
127     * ServiceCallConstants.SERVICE_HOST
128     */
129    public ServiceCallExpressionConfiguration hostHeader(String hostHeader) {
130        setHostHeader(hostHeader);
131        return this;
132    }
133
134    /**
135     * The header that holds the service port information, default
136     * ServiceCallConstants.SERVICE_PORT
137     */
138    public ServiceCallExpressionConfiguration portHeader(String portHeader) {
139        setPortHeader(portHeader);
140        return this;
141    }
142
143    public ServiceCallExpressionConfiguration expressionType(ExpressionDefinition expressionType) {
144        setExpressionType(expressionType);
145        return this;
146    }
147
148    public ServiceCallExpressionConfiguration expression(Expression expression) {
149        setExpression(expression);
150        return this;
151    }
152
153    // *************************************************************************
154    // Factory
155    // *************************************************************************
156
157    @Override
158    public Expression newInstance(CamelContext camelContext) throws Exception {
159        Expression answer = getExpression();
160        if (answer != null) {
161            return answer;
162        }
163
164        ExpressionDefinition expressionType = getExpressionType();
165        if (expressionType != null && answer == null) {
166            return expressionType.createExpression(camelContext);
167        }
168
169        if (factoryKey != null) {
170            // First try to find the factory from the registry.
171            ServiceExpressionFactory factory = CamelContextHelper.lookup(camelContext, factoryKey, ServiceExpressionFactory.class);
172            if (factory != null) {
173                // If a factory is found in the registry do not re-configure it
174                // as
175                // it should be pre-configured.
176                answer = factory.newInstance(camelContext);
177            } else {
178
179                Class<?> type;
180                try {
181                    // Then use Service factory.
182                    type = camelContext.adapt(ExtendedCamelContext.class).getFactoryFinder(ServiceCallDefinitionConstants.RESOURCE_PATH).findClass(factoryKey).orElse(null);
183                } catch (Exception e) {
184                    throw new NoFactoryAvailableException(ServiceCallDefinitionConstants.RESOURCE_PATH + factoryKey, e);
185                }
186
187                if (type != null) {
188                    if (ServiceExpressionFactory.class.isAssignableFrom(type)) {
189                        factory = (ServiceExpressionFactory)camelContext.getInjector().newInstance(type, false);
190                    } else {
191                        throw new IllegalArgumentException("Resolving Expression: " + factoryKey + " detected type conflict: Not a ExpressionFactory implementation. Found: "
192                                                           + type.getName());
193                    }
194                }
195
196                try {
197                    Map<String, Object> parameters = new HashMap<>();
198                    camelContext.adapt(ExtendedCamelContext.class).getBeanIntrospection().getProperties(this, parameters, null, false);
199
200                    parameters.replaceAll((k, v) -> {
201                        if (v instanceof String) {
202                            try {
203                                v = camelContext.resolvePropertyPlaceholders((String)v);
204                            } catch (Exception e) {
205                                throw new IllegalArgumentException(String.format("Exception while resolving %s (%s)", k, v.toString()), e);
206                            }
207                        }
208
209                        return v;
210                    });
211
212                    // Convert properties to Map<String, String>
213                    parameters.put("properties", getPropertiesAsMap(camelContext));
214
215                    postProcessFactoryParameters(camelContext, parameters);
216
217                    PropertyBindingSupport.build().bind(camelContext, factory, parameters);
218
219                    answer = factory.newInstance(camelContext);
220                } catch (Exception e) {
221                    throw new IllegalArgumentException(e);
222                }
223            }
224        }
225
226        return answer;
227    }
228
229}