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