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