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 */
017 package org.apache.camel.builder;
018
019 import java.util.ArrayList;
020 import java.util.Iterator;
021 import java.util.List;
022
023 import org.apache.camel.Endpoint;
024 import org.apache.camel.model.FromDefinition;
025 import org.apache.camel.model.ProcessorDefinition;
026 import org.apache.camel.model.ProcessorDefinitionHelper;
027 import org.apache.camel.model.RouteDefinition;
028 import org.apache.camel.util.EndpointHelper;
029 import org.slf4j.Logger;
030 import org.slf4j.LoggerFactory;
031
032 /**
033 * {@link AdviceWithTask} tasks which are used by the {@link AdviceWithRouteBuilder}.
034 */
035 public final class AdviceWithTasks {
036
037 private static final Logger LOG = LoggerFactory.getLogger(AdviceWithTasks.class);
038
039 private AdviceWithTasks() {
040 // utility class
041 }
042
043 /**
044 * Match by is used for pluggable match by logic.
045 */
046 private interface MatchBy {
047
048 String getId();
049
050 boolean match(ProcessorDefinition<?> processor);
051 }
052
053 /**
054 * Will match by id of the processor.
055 */
056 private static final class MatchById implements MatchBy {
057
058 private final String id;
059
060 private MatchById(String id) {
061 this.id = id;
062 }
063
064 public String getId() {
065 return id;
066 }
067
068 public boolean match(ProcessorDefinition<?> processor) {
069 return EndpointHelper.matchPattern(processor.getId(), id);
070 }
071 }
072
073 /**
074 * Will match by the to string representation of the processor.
075 */
076 private static final class MatchByToString implements MatchBy {
077
078 private final String toString;
079
080 private MatchByToString(String toString) {
081 this.toString = toString;
082 }
083
084 public String getId() {
085 return toString;
086 }
087
088 public boolean match(ProcessorDefinition<?> processor) {
089 return EndpointHelper.matchPattern(processor.toString(), toString);
090 }
091 }
092
093 /**
094 * Will match by the type of the processor.
095 */
096 private static final class MatchByType implements MatchBy {
097
098 private final Class<?> type;
099
100 private MatchByType(Class<?> type) {
101 this.type = type;
102 }
103
104 public String getId() {
105 return type.getSimpleName();
106 }
107
108 public boolean match(ProcessorDefinition<?> processor) {
109 return type.isAssignableFrom(processor.getClass());
110 }
111 }
112
113 public static AdviceWithTask replaceByToString(final RouteDefinition route, final String toString, final ProcessorDefinition<?> replace,
114 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
115 MatchBy matchBy = new MatchByToString(toString);
116 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
117 return doReplace(route, new MatchByToString(toString), replace, it);
118 }
119
120 public static AdviceWithTask replaceById(final RouteDefinition route, final String id, final ProcessorDefinition<?> replace,
121 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
122 MatchBy matchBy = new MatchById(id);
123 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
124 return doReplace(route, matchBy, replace, it);
125 }
126
127 public static AdviceWithTask replaceByType(final RouteDefinition route, final Class<?> type, final ProcessorDefinition<?> replace,
128 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
129 MatchBy matchBy = new MatchByType(type);
130 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
131 return doReplace(route, matchBy, replace, it);
132 }
133
134 private static AdviceWithTask doReplace(final RouteDefinition route, final MatchBy matchBy, final ProcessorDefinition<?> replace,
135 final Iterator<ProcessorDefinition<?>> it) {
136 return new AdviceWithTask() {
137 public void task() throws Exception {
138 boolean match = false;
139 while (it.hasNext()) {
140 ProcessorDefinition<?> output = it.next();
141 if (matchBy.match(output)) {
142 ProcessorDefinition<?> parent = output.getParent();
143 if (parent != null) {
144 int index = parent.getOutputs().indexOf(output);
145 if (index != -1) {
146 match = true;
147 parent.getOutputs().add(index + 1, replace);
148 Object old = parent.getOutputs().remove(index);
149 LOG.info("AdviceWith (" + matchBy.getId() + ") : [" + old + "] --> replace [" + replace + "]");
150 }
151 }
152 }
153 }
154
155 if (!match) {
156 throw new IllegalArgumentException("There are no outputs which matches: " + matchBy.getId() + " in the route: " + route);
157 }
158 }
159 };
160 }
161
162 public static AdviceWithTask removeByToString(final RouteDefinition route, final String toString,
163 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
164 MatchBy matchBy = new MatchByToString(toString);
165 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
166 return doRemove(route, matchBy, it);
167 }
168
169 public static AdviceWithTask removeById(final RouteDefinition route, final String id,
170 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
171 MatchBy matchBy = new MatchById(id);
172 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
173 return doRemove(route, matchBy, it);
174 }
175
176 public static AdviceWithTask removeByType(final RouteDefinition route, final Class<?> type,
177 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
178 MatchBy matchBy = new MatchByType(type);
179 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
180 return doRemove(route, matchBy, it);
181 }
182
183 private static AdviceWithTask doRemove(final RouteDefinition route, final MatchBy matchBy,
184 final Iterator<ProcessorDefinition<?>> it) {
185 return new AdviceWithTask() {
186 public void task() throws Exception {
187 boolean match = false;
188 while (it.hasNext()) {
189 ProcessorDefinition<?> output = it.next();
190 if (matchBy.match(output)) {
191 ProcessorDefinition<?> parent = output.getParent();
192 if (parent != null) {
193 int index = parent.getOutputs().indexOf(output);
194 if (index != -1) {
195 match = true;
196 Object old = parent.getOutputs().remove(index);
197 LOG.info("AdviceWith (" + matchBy.getId() + ") : [" + old + "] --> remove");
198 }
199 }
200 }
201 }
202
203 if (!match) {
204 throw new IllegalArgumentException("There are no outputs which matches: " + matchBy.getId() + " in the route: " + route);
205 }
206 }
207 };
208 }
209
210 public static AdviceWithTask beforeByToString(final RouteDefinition route, final String toString, final ProcessorDefinition<?> before,
211 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
212 MatchBy matchBy = new MatchByToString(toString);
213 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
214 return doBefore(route, matchBy, before, it);
215 }
216
217 public static AdviceWithTask beforeById(final RouteDefinition route, final String id, final ProcessorDefinition<?> before,
218 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
219 MatchBy matchBy = new MatchById(id);
220 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
221 return doBefore(route, matchBy, before, it);
222 }
223
224 public static AdviceWithTask beforeByType(final RouteDefinition route, final Class<?> type, final ProcessorDefinition<?> before,
225 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
226 MatchBy matchBy = new MatchByType(type);
227 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
228 return doBefore(route, matchBy, before, it);
229 }
230
231 private static AdviceWithTask doBefore(final RouteDefinition route, final MatchBy matchBy, final ProcessorDefinition<?> before,
232 final Iterator<ProcessorDefinition<?>> it) {
233 return new AdviceWithTask() {
234 public void task() throws Exception {
235 boolean match = false;
236 while (it.hasNext()) {
237 ProcessorDefinition<?> output = it.next();
238 if (matchBy.match(output)) {
239 ProcessorDefinition<?> parent = output.getParent();
240 if (parent != null) {
241 int index = parent.getOutputs().indexOf(output);
242 if (index != -1) {
243 match = true;
244 Object existing = parent.getOutputs().get(index);
245 parent.getOutputs().add(index, before);
246 LOG.info("AdviceWith (" + matchBy.getId() + ") : [" + existing + "] --> before [" + before + "]");
247 }
248 }
249 }
250 }
251
252 if (!match) {
253 throw new IllegalArgumentException("There are no outputs which matches: " + matchBy.getId() + " in the route: " + route);
254 }
255 }
256 };
257 }
258
259 public static AdviceWithTask afterByToString(final RouteDefinition route, final String toString, final ProcessorDefinition<?> after,
260 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
261 MatchBy matchBy = new MatchByToString(toString);
262 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
263 return doAfter(route, matchBy, after, it);
264 }
265
266 public static AdviceWithTask afterById(final RouteDefinition route, final String id, final ProcessorDefinition<?> after,
267 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
268 MatchBy matchBy = new MatchById(id);
269 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
270 return doAfter(route, matchBy, after, it);
271 }
272
273 public static AdviceWithTask afterByType(final RouteDefinition route, final Class<?> type, final ProcessorDefinition<?> after,
274 boolean selectFirst, boolean selectLast, int selectFrom, int selectTo) {
275 MatchBy matchBy = new MatchByType(type);
276 Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo);
277 return doAfter(route, matchBy, after, it);
278 }
279
280 private static AdviceWithTask doAfter(final RouteDefinition route, final MatchBy matchBy, final ProcessorDefinition<?> after,
281 final Iterator<ProcessorDefinition<?>> it) {
282 return new AdviceWithTask() {
283 public void task() throws Exception {
284 boolean match = false;
285 while (it.hasNext()) {
286 ProcessorDefinition<?> output = it.next();
287 if (matchBy.match(output)) {
288
289 ProcessorDefinition<?> parent = output.getParent();
290 if (parent != null) {
291 int index = parent.getOutputs().indexOf(output);
292 if (index != -1) {
293 match = true;
294 Object existing = parent.getOutputs().get(index);
295 parent.getOutputs().add(index + 1, after);
296 LOG.info("AdviceWith (" + matchBy.getId() + ") : [" + existing + "] --> after [" + after + "]");
297 }
298 }
299 }
300 }
301
302 if (!match) {
303 throw new IllegalArgumentException("There are no outputs which matches: " + matchBy.getId() + " in the route: " + route);
304 }
305 }
306 };
307 }
308
309 public static AdviceWithTask replaceFromWith(final RouteDefinition route, final String uri) {
310 return new AdviceWithTask() {
311 public void task() throws Exception {
312 FromDefinition from = route.getInputs().get(0);
313 LOG.info("AdviceWith replace input from [{}] --> [{}]", from.getUriOrRef(), uri);
314 from.setEndpoint(null);
315 from.setRef(null);
316 from.setUri(uri);
317 }
318 };
319 }
320
321 public static AdviceWithTask replaceFrom(final RouteDefinition route, final Endpoint endpoint) {
322 return new AdviceWithTask() {
323 public void task() throws Exception {
324 FromDefinition from = route.getInputs().get(0);
325 LOG.info("AdviceWith replace input from [{}] --> [{}]", from.getUriOrRef(), endpoint.getEndpointUri());
326 from.setRef(null);
327 from.setUri(null);
328 from.setEndpoint(endpoint);
329 }
330 };
331 }
332
333 /**
334 * Create iterator which walks the route, and only returns nodes which matches the given set of criteria.
335 *
336 * @param route the route
337 * @param matchBy match by which must match
338 * @param selectFirst optional to select only the first
339 * @param selectLast optional to select only the last
340 * @param selectFrom optional to select index/range
341 * @param selectTo optional to select index/range
342 *
343 * @return the iterator
344 */
345 private static Iterator<ProcessorDefinition<?>> createMatchByIterator(final RouteDefinition route, final MatchBy matchBy,
346 final boolean selectFirst, final boolean selectLast,
347 final int selectFrom, final int selectTo) {
348
349 // first iterator and apply match by
350 List<ProcessorDefinition<?>> matched = new ArrayList<ProcessorDefinition<?>>();
351
352 @SuppressWarnings("rawtypes")
353 Iterator<ProcessorDefinition> itAll = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class);
354 while (itAll.hasNext()) {
355 ProcessorDefinition<?> next = itAll.next();
356 if (matchBy.match(next)) {
357 matched.add(next);
358 }
359 }
360
361 // and then apply the selector iterator
362 return createSelectorIterator(matched, selectFirst, selectLast, selectFrom, selectTo);
363 }
364
365 private static Iterator<ProcessorDefinition<?>> createSelectorIterator(final List<ProcessorDefinition<?>> list, final boolean selectFirst,
366 final boolean selectLast, final int selectFrom, final int selectTo) {
367 return new Iterator<ProcessorDefinition<?>>() {
368 private int current;
369 private boolean done;
370
371 @Override
372 public boolean hasNext() {
373 if (list.isEmpty() || done) {
374 return false;
375 }
376
377 if (selectFirst) {
378 done = true;
379 // spool to first
380 current = 0;
381 return true;
382 }
383
384 if (selectLast) {
385 done = true;
386 // spool to last
387 current = list.size() - 1;
388 return true;
389 }
390
391 if (selectFrom >= 0 && selectTo >= 0) {
392 // check for out of bounds
393 if (selectFrom >= list.size() || selectTo >= list.size()) {
394 return false;
395 }
396 if (current < selectFrom) {
397 // spool to beginning of range
398 current = selectFrom;
399 }
400 return current >= selectFrom && current <= selectTo;
401 }
402
403 return current < list.size();
404 }
405
406 @Override
407 public ProcessorDefinition<?> next() {
408 ProcessorDefinition<?> answer = list.get(current);
409 current++;
410 return answer;
411 }
412
413 @Override
414 public void remove() {
415 // noop
416 }
417 };
418 }
419
420 }