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; 018 019import java.util.AbstractList; 020import java.util.ArrayList; 021import java.util.List; 022import javax.xml.bind.annotation.XmlAccessType; 023import javax.xml.bind.annotation.XmlAccessorType; 024import javax.xml.bind.annotation.XmlElement; 025import javax.xml.bind.annotation.XmlElementRef; 026import javax.xml.bind.annotation.XmlRootElement; 027 028import org.apache.camel.Predicate; 029import org.apache.camel.Processor; 030import org.apache.camel.builder.ExpressionClause; 031import org.apache.camel.model.language.ExpressionDefinition; 032import org.apache.camel.processor.ChoiceProcessor; 033import org.apache.camel.processor.FilterProcessor; 034import org.apache.camel.spi.AsPredicate; 035import org.apache.camel.spi.Metadata; 036import org.apache.camel.spi.RouteContext; 037import org.apache.camel.util.CollectionStringBuffer; 038import org.apache.camel.util.ObjectHelper; 039 040/** 041 * Routes messages based on a series of predicates 042 * 043 * @version 044 */ 045@Metadata(label = "eip,routing") 046@XmlRootElement(name = "choice") 047@XmlAccessorType(XmlAccessType.FIELD) 048public class ChoiceDefinition extends ProcessorDefinition<ChoiceDefinition> { 049 @XmlElementRef @AsPredicate 050 private List<WhenDefinition> whenClauses = new ArrayList<WhenDefinition>(); 051 @XmlElement 052 private OtherwiseDefinition otherwise; 053 054 private transient boolean onlyWhenOrOtherwise = true; 055 056 public ChoiceDefinition() { 057 } 058 059 @Override 060 public List<ProcessorDefinition<?>> getOutputs() { 061 // wrap the outputs into a list where we can on the inside control the when/otherwise 062 // but make it appear as a list on the outside 063 return new AbstractList<ProcessorDefinition<?>>() { 064 065 public ProcessorDefinition<?> get(int index) { 066 if (index < whenClauses.size()) { 067 return whenClauses.get(index); 068 } 069 if (index == whenClauses.size()) { 070 return otherwise; 071 } 072 throw new IndexOutOfBoundsException("Index " + index + " is out of bounds with size " + size()); 073 } 074 075 public boolean add(ProcessorDefinition<?> def) { 076 if (def instanceof WhenDefinition) { 077 return whenClauses.add((WhenDefinition)def); 078 } else if (def instanceof OtherwiseDefinition) { 079 otherwise = (OtherwiseDefinition)def; 080 return true; 081 } 082 throw new IllegalArgumentException("Expected either a WhenDefinition or OtherwiseDefinition but was " 083 + ObjectHelper.classCanonicalName(def)); 084 } 085 086 public int size() { 087 return whenClauses.size() + (otherwise == null ? 0 : 1); 088 } 089 090 public void clear() { 091 whenClauses.clear(); 092 otherwise = null; 093 } 094 095 public ProcessorDefinition<?> set(int index, ProcessorDefinition<?> element) { 096 if (index < whenClauses.size()) { 097 if (element instanceof WhenDefinition) { 098 return whenClauses.set(index, (WhenDefinition)element); 099 } 100 throw new IllegalArgumentException("Expected WhenDefinition but was " 101 + ObjectHelper.classCanonicalName(element)); 102 } else if (index == whenClauses.size()) { 103 ProcessorDefinition<?> old = otherwise; 104 otherwise = (OtherwiseDefinition)element; 105 return old; 106 } 107 throw new IndexOutOfBoundsException("Index " + index + " is out of bounds with size " + size()); 108 } 109 110 public ProcessorDefinition<?> remove(int index) { 111 if (index < whenClauses.size()) { 112 return whenClauses.remove(index); 113 } else if (index == whenClauses.size()) { 114 ProcessorDefinition<?> old = otherwise; 115 otherwise = null; 116 return old; 117 } 118 throw new IndexOutOfBoundsException("Index " + index + " is out of bounds with size " + size()); 119 } 120 }; 121 } 122 123 @Override 124 public boolean isOutputSupported() { 125 return true; 126 } 127 128 @Override 129 public String toString() { 130 return "Choice[" + getWhenClauses() + (getOtherwise() != null ? " " + getOtherwise() : "") + "]"; 131 } 132 133 @Override 134 public Processor createProcessor(RouteContext routeContext) throws Exception { 135 List<FilterProcessor> filters = new ArrayList<FilterProcessor>(); 136 for (WhenDefinition whenClause : whenClauses) { 137 // also resolve properties and constant fields on embedded expressions in the when clauses 138 ExpressionNode exp = whenClause; 139 ExpressionDefinition expressionDefinition = exp.getExpression(); 140 if (expressionDefinition != null) { 141 // resolve properties before we create the processor 142 ProcessorDefinitionHelper.resolvePropertyPlaceholders(routeContext.getCamelContext(), expressionDefinition); 143 144 // resolve constant fields (eg Exchange.FILE_NAME) 145 ProcessorDefinitionHelper.resolveKnownConstantFields(expressionDefinition); 146 } 147 148 FilterProcessor filter = (FilterProcessor) createProcessor(routeContext, whenClause); 149 filters.add(filter); 150 } 151 Processor otherwiseProcessor = null; 152 if (otherwise != null) { 153 otherwiseProcessor = createProcessor(routeContext, otherwise); 154 } 155 return new ChoiceProcessor(filters, otherwiseProcessor); 156 } 157 158 @Override 159 public void addOutput(ProcessorDefinition<?> output) { 160 if (onlyWhenOrOtherwise) { 161 if (output instanceof WhenDefinition || output instanceof OtherwiseDefinition) { 162 // okay we are adding a when or otherwise so allow any kind of output after this again 163 onlyWhenOrOtherwise = false; 164 } else { 165 throw new IllegalArgumentException("A new choice clause should start with a when() or otherwise(). " 166 + "If you intend to end the entire choice and are using endChoice() then use end() instead."); 167 } 168 } 169 super.addOutput(output); 170 } 171 172 @Override 173 public ProcessorDefinition<?> end() { 174 // we end a block so only when or otherwise is supported 175 onlyWhenOrOtherwise = true; 176 return super.end(); 177 } 178 179 @Override 180 public ChoiceDefinition endChoice() { 181 // we end a block so only when or otherwise is supported 182 onlyWhenOrOtherwise = true; 183 return super.endChoice(); 184 } 185 186 // Fluent API 187 // ------------------------------------------------------------------------- 188 189 /** 190 * Sets the predicate for the when node 191 * 192 * @param predicate the predicate 193 * @return the builder 194 */ 195 public ChoiceDefinition when(@AsPredicate Predicate predicate) { 196 addClause(new WhenDefinition(predicate)); 197 return this; 198 } 199 200 /** 201 * Creates an expression for the when node 202 * 203 * @return expression to be used as builder to configure the when node 204 */ 205 @AsPredicate 206 public ExpressionClause<ChoiceDefinition> when() { 207 ExpressionClause<ChoiceDefinition> clause = new ExpressionClause<ChoiceDefinition>(this); 208 addClause(new WhenDefinition(clause)); 209 return clause; 210 } 211 212 private void addClause(ProcessorDefinition<?> when) { 213 onlyWhenOrOtherwise = true; 214 popBlock(); 215 addOutput(when); 216 pushBlock(when); 217 } 218 219 /** 220 * Sets the otherwise node 221 * 222 * @return the builder 223 */ 224 public ChoiceDefinition otherwise() { 225 OtherwiseDefinition answer = new OtherwiseDefinition(); 226 addClause(answer); 227 return this; 228 } 229 230 @Override 231 public void setId(String value) { 232 // when setting id, we should set it on the fine grained element, if possible 233 if (otherwise != null) { 234 otherwise.setId(value); 235 } else if (!getWhenClauses().isEmpty()) { 236 int size = getWhenClauses().size(); 237 getWhenClauses().get(size - 1).setId(value); 238 } else { 239 super.setId(value); 240 } 241 } 242 243 // Properties 244 // ------------------------------------------------------------------------- 245 246 @Override 247 public String getLabel() { 248 CollectionStringBuffer buffer = new CollectionStringBuffer("choice["); 249 List<WhenDefinition> list = getWhenClauses(); 250 for (WhenDefinition whenType : list) { 251 buffer.append(whenType.getLabel()); 252 } 253 buffer.append("]"); 254 return buffer.toString(); 255 } 256 257 public List<WhenDefinition> getWhenClauses() { 258 return whenClauses; 259 } 260 261 /** 262 * Sets the when clauses 263 */ 264 public void setWhenClauses(List<WhenDefinition> whenClauses) { 265 this.whenClauses = whenClauses; 266 } 267 268 public OtherwiseDefinition getOtherwise() { 269 return otherwise; 270 } 271 272 public void setOtherwise(OtherwiseDefinition otherwise) { 273 this.otherwise = otherwise; 274 } 275 276 @Override 277 public void configureChild(ProcessorDefinition<?> output) { 278 if (whenClauses == null || whenClauses.isEmpty()) { 279 return; 280 } 281 for (WhenDefinition when : whenClauses) { 282 if (when.getExpression() instanceof ExpressionClause) { 283 ExpressionClause<?> clause = (ExpressionClause<?>) when.getExpression(); 284 if (clause.getExpressionType() != null) { 285 // if using the Java DSL then the expression may have been set using the 286 // ExpressionClause which is a fancy builder to define expressions and predicates 287 // using fluent builders in the DSL. However we need afterwards a callback to 288 // reset the expression to the expression type the ExpressionClause did build for us 289 when.setExpression(clause.getExpressionType()); 290 } 291 } 292 } 293 } 294}