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; 025 026import javax.xml.bind.annotation.XmlAccessType; 027import javax.xml.bind.annotation.XmlAccessorType; 028import javax.xml.bind.annotation.XmlAttribute; 029import javax.xml.bind.annotation.XmlElement; 030import javax.xml.bind.annotation.XmlElementRef; 031import javax.xml.bind.annotation.XmlRootElement; 032import javax.xml.bind.annotation.XmlTransient; 033 034import org.apache.camel.CamelContext; 035import org.apache.camel.Expression; 036import org.apache.camel.NoFactoryAvailableException; 037import org.apache.camel.cloud.ServiceExpressionFactory; 038import org.apache.camel.impl.cloud.DefaultServiceCallExpression; 039import org.apache.camel.impl.cloud.ServiceCallConstants; 040import org.apache.camel.model.IdentifiedType; 041import org.apache.camel.model.ProcessorDefinition; 042import org.apache.camel.model.PropertyDefinition; 043import org.apache.camel.model.language.ExpressionDefinition; 044import org.apache.camel.spi.Metadata; 045import org.apache.camel.util.CamelContextHelper; 046import org.apache.camel.util.IntrospectionSupport; 047import org.apache.camel.util.ObjectHelper; 048 049@Metadata(label = "routing,cloud") 050@XmlRootElement(name = "serviceExpression") 051@XmlAccessorType(XmlAccessType.FIELD) 052public class ServiceCallExpressionConfiguration extends IdentifiedType implements ServiceExpressionFactory { 053 @XmlTransient 054 private final ServiceCallDefinition parent; 055 @XmlTransient 056 private final String factoryKey; 057 @XmlElement(name = "properties") @Metadata(label = "advanced") 058 private List<PropertyDefinition> properties; 059 @XmlAttribute @Metadata(defaultValue = ServiceCallConstants.SERVICE_HOST) 060 private String hostHeader = ServiceCallConstants.SERVICE_HOST; 061 @XmlAttribute @Metadata(defaultValue = ServiceCallConstants.SERVICE_PORT) 062 private String portHeader = ServiceCallConstants.SERVICE_PORT; 063 @XmlElementRef(required = false) 064 private ExpressionDefinition expressionType; 065 @XmlTransient 066 private Expression expression; 067 068 public ServiceCallExpressionConfiguration() { 069 this(null, null); 070 } 071 072 public ServiceCallExpressionConfiguration(ServiceCallDefinition parent, String factoryKey) { 073 this.parent = parent; 074 this.factoryKey = factoryKey; 075 } 076 077 public ServiceCallDefinition end() { 078 return this.parent; 079 } 080 081 public ProcessorDefinition<?> endParent() { 082 return this.parent.end(); 083 } 084 085 // ************************************************************************* 086 // 087 // ************************************************************************* 088 089 public List<PropertyDefinition> getProperties() { 090 return properties; 091 } 092 093 /** 094 * Set client properties to use. 095 * <p/> 096 * These properties are specific to what service call implementation are in 097 * use. For example if using ribbon, then the client properties are define 098 * in com.netflix.client.config.CommonClientConfigKey. 099 */ 100 public void setProperties(List<PropertyDefinition> properties) { 101 this.properties = properties; 102 } 103 104 /** 105 * Adds a custom property to use. 106 * <p/> 107 * These properties are specific to what service call implementation are in 108 * use. For example if using ribbon, then the client properties are define 109 * in com.netflix.client.config.CommonClientConfigKey. 110 */ 111 public ServiceCallExpressionConfiguration property(String key, String value) { 112 if (properties == null) { 113 properties = new ArrayList<>(); 114 } 115 PropertyDefinition prop = new PropertyDefinition(); 116 prop.setKey(key); 117 prop.setValue(value); 118 properties.add(prop); 119 return this; 120 } 121 122 protected Map<String, String> getPropertiesAsMap(CamelContext camelContext) throws Exception { 123 Map<String, String> answer; 124 125 if (properties == null || properties.isEmpty()) { 126 answer = Collections.emptyMap(); 127 } else { 128 answer = new HashMap<>(); 129 for (PropertyDefinition prop : properties) { 130 // support property placeholders 131 String key = CamelContextHelper.parseText(camelContext, prop.getKey()); 132 String value = CamelContextHelper.parseText(camelContext, prop.getValue()); 133 answer.put(key, value); 134 } 135 } 136 137 return answer; 138 } 139 140 public String getHostHeader() { 141 return hostHeader; 142 } 143 144 /** 145 * The header that holds the service host information, default ServiceCallConstants.SERVICE_HOST 146 */ 147 public void setHostHeader(String hostHeader) { 148 this.hostHeader = hostHeader; 149 } 150 151 public String getPortHeader() { 152 return portHeader; 153 } 154 155 /** 156 * The header that holds the service port information, default ServiceCallConstants.SERVICE_PORT 157 */ 158 public void setPortHeader(String portHeader) { 159 this.portHeader = portHeader; 160 } 161 162 public ExpressionDefinition getExpressionType() { 163 return expressionType; 164 } 165 166 public void setExpressionType(ExpressionDefinition expressionType) { 167 this.expressionType = expressionType; 168 } 169 170 public Expression getExpression() { 171 return expression; 172 } 173 174 public void setExpression(Expression expression) { 175 this.expression = expression; 176 } 177 178 /** 179 * The header that holds the service host information, default ServiceCallConstants.SERVICE_HOST 180 */ 181 public ServiceCallExpressionConfiguration hostHeader(String hostHeader) { 182 setHostHeader(hostHeader); 183 return this; 184 } 185 186 /** 187 * The header that holds the service port information, default ServiceCallConstants.SERVICE_PORT 188 */ 189 public ServiceCallExpressionConfiguration portHeader(String portHeader) { 190 setPortHeader(portHeader); 191 return this; 192 } 193 194 public ServiceCallExpressionConfiguration expressionType(ExpressionDefinition expressionType) { 195 setExpressionType(expressionType); 196 return this; 197 } 198 199 public ServiceCallExpressionConfiguration expression(Expression expression) { 200 setExpression(expression); 201 return this; 202 } 203 204 // ************************************************************************* 205 // Factory 206 // ************************************************************************* 207 208 @Override 209 public Expression newInstance(CamelContext camelContext) throws Exception { 210 Expression answer = getExpression(); 211 if (answer != null) { 212 return answer; 213 } 214 215 ExpressionDefinition expressionType = getExpressionType(); 216 if (expressionType != null && answer == null) { 217 return expressionType.createExpression(camelContext); 218 } 219 220 if (factoryKey != null) { 221 // First try to find the factory from the registry. 222 ServiceExpressionFactory factory = CamelContextHelper.lookup(camelContext, factoryKey, ServiceExpressionFactory.class); 223 if (factory != null) { 224 // If a factory is found in the registry do not re-configure it as 225 // it should be pre-configured. 226 answer = factory.newInstance(camelContext); 227 } else { 228 229 Class<?> type; 230 try { 231 // Then use Service factory. 232 type = camelContext.getFactoryFinder(ServiceCallDefinitionConstants.RESOURCE_PATH).findClass(factoryKey); 233 } catch (Exception e) { 234 throw new NoFactoryAvailableException(ServiceCallDefinitionConstants.RESOURCE_PATH + factoryKey, e); 235 } 236 237 if (type != null) { 238 if (ServiceExpressionFactory.class.isAssignableFrom(type)) { 239 factory = (ServiceExpressionFactory) camelContext.getInjector().newInstance(type); 240 } else { 241 throw new IllegalArgumentException( 242 "Resolving Expression: " + factoryKey + " detected type conflict: Not a ExpressionFactory implementation. Found: " + type.getName()); 243 } 244 } 245 246 try { 247 Map<String, Object> parameters = new HashMap<>(); 248 IntrospectionSupport.getProperties(this, parameters, null, false); 249 250 parameters.replaceAll( 251 (k, v) -> { 252 if (v instanceof String) { 253 try { 254 v = camelContext.resolvePropertyPlaceholders((String) v); 255 } catch (Exception e) { 256 throw new IllegalArgumentException( 257 String.format("Exception while resolving %s (%s)", k, v.toString()), 258 e 259 ); 260 } 261 } 262 263 return v; 264 } 265 ); 266 267 // Convert properties to Map<String, String> 268 parameters.put("properties", getPropertiesAsMap(camelContext)); 269 270 postProcessFactoryParameters(camelContext, parameters); 271 272 IntrospectionSupport.setProperties(factory, parameters); 273 274 answer = factory.newInstance(camelContext); 275 } catch (Exception e) { 276 throw new IllegalArgumentException(e); 277 } 278 } 279 } else { 280 answer = new DefaultServiceCallExpression( 281 ObjectHelper.notNull(hostHeader, "hostHeader"), 282 ObjectHelper.notNull(portHeader, "portHeader") 283 ); 284 } 285 286 return answer; 287 } 288 289 // ************************************************************************* 290 // Utilities 291 // ************************************************************************* 292 293 protected void postProcessFactoryParameters(CamelContext camelContext, Map<String, Object> parameters) throws Exception { 294 } 295}