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