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