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