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