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