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.language; 018 019import java.util.List; 020import java.util.Map; 021 022import javax.xml.bind.annotation.XmlAccessType; 023import javax.xml.bind.annotation.XmlAccessorType; 024import javax.xml.bind.annotation.XmlAnyAttribute; 025import javax.xml.bind.annotation.XmlAttribute; 026import javax.xml.bind.annotation.XmlID; 027import javax.xml.bind.annotation.XmlRootElement; 028import javax.xml.bind.annotation.XmlTransient; 029import javax.xml.bind.annotation.XmlType; 030import javax.xml.bind.annotation.XmlValue; 031import javax.xml.namespace.QName; 032 033import org.apache.camel.AfterPropertiesConfigured; 034import org.apache.camel.CamelContext; 035import org.apache.camel.CamelContextAware; 036import org.apache.camel.Exchange; 037import org.apache.camel.Expression; 038import org.apache.camel.ExpressionFactory; 039import org.apache.camel.ExtendedCamelContext; 040import org.apache.camel.NoSuchLanguageException; 041import org.apache.camel.Predicate; 042import org.apache.camel.model.DefinitionPropertyPlaceholderConfigurer; 043import org.apache.camel.model.OtherAttributesAware; 044import org.apache.camel.spi.Language; 045import org.apache.camel.spi.Metadata; 046import org.apache.camel.spi.RouteContext; 047import org.apache.camel.support.ExpressionToPredicateAdapter; 048import org.apache.camel.support.ScriptHelper; 049import org.apache.camel.util.CollectionStringBuffer; 050import org.apache.camel.util.ObjectHelper; 051 052/** 053 * A useful base class for an expression 054 */ 055@Metadata(label = "language", title = "Expression") 056@XmlRootElement 057@XmlType(name = "expression") // must be named expression 058@XmlAccessorType(XmlAccessType.FIELD) 059public class ExpressionDefinition implements Expression, Predicate, OtherAttributesAware, ExpressionFactory, DefinitionPropertyPlaceholderConfigurer { 060 @XmlAttribute 061 @XmlID 062 private String id; 063 @XmlValue 064 @Metadata(required = true) 065 private String expression; 066 @XmlAttribute 067 @Metadata(defaultValue = "true") 068 private Boolean trim; 069 @XmlTransient 070 private Predicate predicate; 071 @XmlTransient 072 private Expression expressionValue; 073 @XmlTransient 074 private ExpressionDefinition expressionType; 075 // use xs:any to support optional property placeholders 076 @XmlAnyAttribute 077 private Map<QName, Object> otherAttributes; 078 079 public ExpressionDefinition() { 080 } 081 082 public ExpressionDefinition(String expression) { 083 this.expression = expression; 084 } 085 086 public ExpressionDefinition(Predicate predicate) { 087 this.predicate = predicate; 088 } 089 090 public ExpressionDefinition(Expression expression) { 091 this.expressionValue = expression; 092 } 093 094 public static String getLabel(List<ExpressionDefinition> expressions) { 095 CollectionStringBuffer buffer = new CollectionStringBuffer(); 096 for (ExpressionDefinition expression : expressions) { 097 buffer.append(expression.getLabel()); 098 } 099 return buffer.toString(); 100 } 101 102 @Override 103 public String toString() { 104 // favour using the output from expression value 105 if (getExpressionValue() != null) { 106 return getExpressionValue().toString(); 107 } 108 109 StringBuilder sb = new StringBuilder(); 110 if (getLanguage() != null) { 111 sb.append(getLanguage()).append("{"); 112 } 113 if (getPredicate() != null) { 114 sb.append(getPredicate().toString()); 115 } else if (getExpression() != null) { 116 sb.append(getExpression()); 117 } 118 if (getLanguage() != null) { 119 sb.append("}"); 120 } 121 return sb.toString(); 122 } 123 124 public Object evaluate(Exchange exchange) { 125 return evaluate(exchange, Object.class); 126 } 127 128 @Override 129 public <T> T evaluate(Exchange exchange, Class<T> type) { 130 if (expressionValue == null) { 131 expressionValue = createExpression(exchange.getContext()); 132 } 133 ObjectHelper.notNull(expressionValue, "expressionValue"); 134 return expressionValue.evaluate(exchange, type); 135 } 136 137 public void assertMatches(String text, Exchange exchange) throws AssertionError { 138 if (!matches(exchange)) { 139 throw new AssertionError(text + getExpression() + " for exchange: " + exchange); 140 } 141 } 142 143 @Override 144 public boolean matches(Exchange exchange) { 145 if (predicate == null) { 146 predicate = createPredicate(exchange.getContext()); 147 } 148 ObjectHelper.notNull(predicate, "predicate"); 149 return predicate.matches(exchange); 150 } 151 152 public String getLanguage() { 153 return ""; 154 } 155 156 public final Predicate createPredicate(RouteContext routeContext) { 157 return createPredicate(routeContext.getCamelContext()); 158 } 159 160 public Predicate createPredicate(CamelContext camelContext) { 161 if (predicate == null) { 162 if (getExpressionType() != null) { 163 predicate = getExpressionType().createPredicate(camelContext); 164 } else if (getExpressionValue() != null) { 165 predicate = new ExpressionToPredicateAdapter(getExpressionValue()); 166 } else if (getExpression() != null) { 167 ObjectHelper.notNull("language", getLanguage()); 168 Language language = camelContext.resolveLanguage(getLanguage()); 169 if (language == null) { 170 throw new NoSuchLanguageException(getLanguage()); 171 } 172 String exp = getExpression(); 173 // should be true by default 174 boolean isTrim = getTrim() == null || getTrim(); 175 // trim if configured to trim 176 if (exp != null && isTrim) { 177 exp = exp.trim(); 178 } 179 // resolve the expression as it may be an external script from 180 // the classpath/file etc 181 exp = ScriptHelper.resolveOptionalExternalScript(camelContext, exp); 182 183 predicate = language.createPredicate(exp); 184 configurePredicate(camelContext, predicate); 185 } 186 } 187 // inject CamelContext if its aware 188 if (predicate instanceof CamelContextAware) { 189 ((CamelContextAware)predicate).setCamelContext(camelContext); 190 } 191 return predicate; 192 } 193 194 public final Expression createExpression(RouteContext routeContext) { 195 return createExpression(routeContext.getCamelContext()); 196 } 197 198 @Override 199 public Expression createExpression(CamelContext camelContext) { 200 if (getExpressionValue() == null) { 201 if (getExpressionType() != null) { 202 setExpressionValue(getExpressionType().createExpression(camelContext)); 203 } else if (getExpression() != null) { 204 ObjectHelper.notNull("language", getLanguage()); 205 Language language = camelContext.resolveLanguage(getLanguage()); 206 if (language == null) { 207 throw new NoSuchLanguageException(getLanguage()); 208 } 209 String exp = getExpression(); 210 // should be true by default 211 boolean isTrim = getTrim() == null || getTrim(); 212 // trim if configured to trim 213 if (exp != null && isTrim) { 214 exp = exp.trim(); 215 } 216 // resolve the expression as it may be an external script from 217 // the classpath/file etc 218 exp = ScriptHelper.resolveOptionalExternalScript(camelContext, exp); 219 220 setExpressionValue(language.createExpression(exp)); 221 configureExpression(camelContext, getExpressionValue()); 222 } 223 } 224 // inject CamelContext if its aware 225 if (getExpressionValue() instanceof CamelContextAware) { 226 ((CamelContextAware)getExpressionValue()).setCamelContext(camelContext); 227 } 228 return getExpressionValue(); 229 } 230 231 public String getExpression() { 232 return expression; 233 } 234 235 /** 236 * The expression value in your chosen language syntax 237 */ 238 public void setExpression(String expression) { 239 this.expression = expression; 240 } 241 242 public String getId() { 243 return id; 244 } 245 246 /** 247 * Sets the id of this node 248 */ 249 public void setId(String value) { 250 this.id = value; 251 } 252 253 public Predicate getPredicate() { 254 return predicate; 255 } 256 257 public Expression getExpressionValue() { 258 return expressionValue; 259 } 260 261 protected void setExpressionValue(Expression expressionValue) { 262 this.expressionValue = expressionValue; 263 } 264 265 public ExpressionDefinition getExpressionType() { 266 return expressionType; 267 } 268 269 public Boolean getTrim() { 270 return trim; 271 } 272 273 /** 274 * Whether to trim the value to remove leading and trailing whitespaces and 275 * line breaks 276 */ 277 public void setTrim(Boolean trim) { 278 this.trim = trim; 279 } 280 281 @Override 282 public Map<QName, Object> getOtherAttributes() { 283 return otherAttributes; 284 } 285 286 @Override 287 public void setOtherAttributes(Map<QName, Object> otherAttributes) { 288 this.otherAttributes = otherAttributes; 289 } 290 291 /** 292 * Returns some descriptive text to describe this node 293 */ 294 public String getLabel() { 295 Predicate predicate = getPredicate(); 296 if (predicate != null) { 297 return predicate.toString(); 298 } 299 Expression expressionValue = getExpressionValue(); 300 if (expressionValue != null) { 301 return expressionValue.toString(); 302 } 303 304 String exp = getExpression(); 305 return exp != null ? exp : ""; 306 } 307 308 /** 309 * Allows derived classes to set a lazily created expressionType instance 310 * such as if using the {@link org.apache.camel.builder.ExpressionClause} 311 */ 312 protected void setExpressionType(ExpressionDefinition expressionType) { 313 this.expressionType = expressionType; 314 } 315 316 protected void configurePredicate(CamelContext camelContext, Predicate predicate) { 317 // allows to perform additional logic after the properties has been 318 // configured which may be needed 319 // in the various camel components outside camel-core 320 if (predicate instanceof AfterPropertiesConfigured) { 321 ((AfterPropertiesConfigured)predicate).afterPropertiesConfigured(camelContext); 322 } 323 } 324 325 protected void configureExpression(CamelContext camelContext, Expression expression) { 326 // allows to perform additional logic after the properties has been 327 // configured which may be needed 328 // in the various camel components outside camel-core 329 if (expression instanceof AfterPropertiesConfigured) { 330 ((AfterPropertiesConfigured)expression).afterPropertiesConfigured(camelContext); 331 } 332 } 333 334 /** 335 * Sets a named property on the object instance using introspection 336 */ 337 protected void setProperty(CamelContext camelContext, Object bean, String name, Object value) { 338 try { 339 camelContext.adapt(ExtendedCamelContext.class).getBeanIntrospection().setProperty(camelContext, bean, name, value); 340 } catch (Exception e) { 341 throw new IllegalArgumentException("Failed to set property " + name + " on " + bean + ". Reason: " + e, e); 342 } 343 } 344}