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