/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.builder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.camel.Endpoint;
import org.apache.camel.model.ChoiceDefinition;
import org.apache.camel.model.EndpointRequiredDefinition;
import org.apache.camel.model.FromDefinition;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.model.ProcessorDefinitionHelper;
import org.apache.camel.model.RouteDefinition;
import org.apache.camel.util.EndpointHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link AdviceWithTask} tasks which are used by the {@link AdviceWithRouteBuilder}.
*/
public final class AdviceWithTasks {
private static final Logger LOG = LoggerFactory.getLogger(AdviceWithTasks.class);
private AdviceWithTasks() {
// utility class
}
/**
* Match by is used for pluggable match by logic.
*/
private interface MatchBy {
String getId();
boolean match(ProcessorDefinition<?> processor);
}
/**
* Will match by id of the processor.
*/
private static final class MatchById implements MatchBy {
private final String id;
private MatchById(String id) {
this.id = id;
}
public String getId() {
return id;
}
public boolean match(ProcessorDefinition<?> processor) {
if (id.equals("*")) {
// make sure the processor which id isn't be set is matched.
return true;
}
return EndpointHelper.matchPattern(processor.getId(), id);
}
}
/**
* Will match by the to string representation of the processor.
*/
private static final class MatchByToString implements MatchBy {
private final String toString;
private MatchByToString(String toString) {
this.toString = toString;
}
public String getId() {
return toString;
}
public boolean match(ProcessorDefinition<?> processor) {
return EndpointHelper.matchPattern(processor.toString(), toString);
}
}
/**
* Will match by the sending to endpoint uri representation of the processor.
*/
private static final class MatchByToUri implements MatchBy {
private final String toUri;
private MatchByToUri(String toUri) {
this.toUri = toUri;
}
public String getId() {
return toUri;
}
public boolean match(ProcessorDefinition<?> processor) {
if (processor instanceof EndpointRequiredDefinition) {
String uri = ((EndpointRequiredDefinition) processor).getEndpointUri();
return EndpointHelper.matchPattern(uri, toUri);
}
return false;
}
}
/**
* Will match by the type of the processor.
*/
private static final class MatchByType implements MatchBy {
private final Class<?> type;
private MatchByType(Class<?> type) {
this.type = type;
}
public String getId() {
return type.getSimpleName();
}
public boolean match(ProcessorDefinition<?> processor) {
return type.isAssignableFrom(processor.getClass());
}
}
public static AdviceWithTask replaceByToString(final RouteDefinition route, final String toString, final ProcessorDefinition<?> replace,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByToString(toString);
return doReplace(route, matchBy, replace, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask replaceByToUri(final RouteDefinition route, final String toUri, final ProcessorDefinition<?> replace,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByToUri(toUri);
return doReplace(route, matchBy, replace, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask replaceById(final RouteDefinition route, final String id, final ProcessorDefinition<?> replace,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchById(id);
return doReplace(route, matchBy, replace, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask replaceByType(final RouteDefinition route, final Class<?> type, final ProcessorDefinition<?> replace,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByType(type);
return doReplace(route, matchBy, replace, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
private static AdviceWithTask doReplace(final RouteDefinition route, final MatchBy matchBy, final ProcessorDefinition<?> replace,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
return new AdviceWithTask() {
public void task() throws Exception {
Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
boolean match = false;
while (it.hasNext()) {
ProcessorDefinition<?> output = it.next();
if (matchBy.match(output)) {
List<ProcessorDefinition<?>> outputs = getOutputs(output);
if (outputs != null) {
int index = outputs.indexOf(output);
if (index != -1) {
match = true;
outputs.add(index + 1, replace);
Object old = outputs.remove(index);
// must set parent on the node we added in the route
replace.setParent(output.getParent());
LOG.info("AdviceWith (" + matchBy.getId() + ") : [" + old + "] --> replace [" + replace + "]");
}
}
}
}
if (!match) {
throw new IllegalArgumentException("There are no outputs which matches: " + matchBy.getId() + " in the route: " + route);
}
}
};
}
public static AdviceWithTask removeByToString(final RouteDefinition route, final String toString,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByToString(toString);
return doRemove(route, matchBy, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask removeByToUri(final RouteDefinition route, final String toUri,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByToUri(toUri);
return doRemove(route, matchBy, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask removeById(final RouteDefinition route, final String id,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchById(id);
return doRemove(route, matchBy, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask removeByType(final RouteDefinition route, final Class<?> type,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByType(type);
return doRemove(route, matchBy, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
private static AdviceWithTask doRemove(final RouteDefinition route, final MatchBy matchBy,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
return new AdviceWithTask() {
public void task() throws Exception {
boolean match = false;
Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
while (it.hasNext()) {
ProcessorDefinition<?> output = it.next();
if (matchBy.match(output)) {
List<ProcessorDefinition<?>> outputs = getOutputs(output);
if (outputs != null) {
int index = outputs.indexOf(output);
if (index != -1) {
match = true;
Object old = outputs.remove(index);
LOG.info("AdviceWith (" + matchBy.getId() + ") : [" + old + "] --> remove");
}
}
}
}
if (!match) {
throw new IllegalArgumentException("There are no outputs which matches: " + matchBy.getId() + " in the route: " + route);
}
}
};
}
public static AdviceWithTask beforeByToString(final RouteDefinition route, final String toString, final ProcessorDefinition<?> before,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByToString(toString);
return doBefore(route, matchBy, before, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask beforeByToUri(final RouteDefinition route, final String toUri, final ProcessorDefinition<?> before,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByToUri(toUri);
return doBefore(route, matchBy, before, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask beforeById(final RouteDefinition route, final String id, final ProcessorDefinition<?> before,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchById(id);
return doBefore(route, matchBy, before, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask beforeByType(final RouteDefinition route, final Class<?> type, final ProcessorDefinition<?> before,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByType(type);
return doBefore(route, matchBy, before, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
private static AdviceWithTask doBefore(final RouteDefinition route, final MatchBy matchBy, final ProcessorDefinition<?> before,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
return new AdviceWithTask() {
public void task() throws Exception {
boolean match = false;
Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
while (it.hasNext()) {
ProcessorDefinition<?> output = it.next();
if (matchBy.match(output)) {
List<ProcessorDefinition<?>> outputs = getOutputs(output);
if (outputs != null) {
int index = outputs.indexOf(output);
if (index != -1) {
match = true;
Object existing = outputs.get(index);
outputs.add(index, before);
// must set parent on the node we added in the route
before.setParent(output.getParent());
LOG.info("AdviceWith (" + matchBy.getId() + ") : [" + existing + "] --> before [" + before + "]");
}
}
}
}
if (!match) {
throw new IllegalArgumentException("There are no outputs which matches: " + matchBy.getId() + " in the route: " + route);
}
}
};
}
public static AdviceWithTask afterByToString(final RouteDefinition route, final String toString, final ProcessorDefinition<?> after,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByToString(toString);
return doAfter(route, matchBy, after, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask afterByToUri(final RouteDefinition route, final String toUri, final ProcessorDefinition<?> after,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByToUri(toUri);
return doAfter(route, matchBy, after, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask afterById(final RouteDefinition route, final String id, final ProcessorDefinition<?> after,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchById(id);
return doAfter(route, matchBy, after, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
public static AdviceWithTask afterByType(final RouteDefinition route, final Class<?> type, final ProcessorDefinition<?> after,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
MatchBy matchBy = new MatchByType(type);
return doAfter(route, matchBy, after, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
}
private static AdviceWithTask doAfter(final RouteDefinition route, final MatchBy matchBy, final ProcessorDefinition<?> after,
boolean selectFirst, boolean selectLast, int selectFrom, int selectTo, int maxDeep) {
return new AdviceWithTask() {
public void task() throws Exception {
boolean match = false;
Iterator<ProcessorDefinition<?>> it = AdviceWithTasks.createMatchByIterator(route, matchBy, selectFirst, selectLast, selectFrom, selectTo, maxDeep);
while (it.hasNext()) {
ProcessorDefinition<?> output = it.next();
if (matchBy.match(output)) {
List<ProcessorDefinition<?>> outputs = getOutputs(output);
if (outputs != null) {
int index = outputs.indexOf(output);
if (index != -1) {
match = true;
Object existing = outputs.get(index);
outputs.add(index + 1, after);
// must set parent on the node we added in the route
after.setParent(output.getParent());
LOG.info("AdviceWith (" + matchBy.getId() + ") : [" + existing + "] --> after [" + after + "]");
}
}
}
}
if (!match) {
throw new IllegalArgumentException("There are no outputs which matches: " + matchBy.getId() + " in the route: " + route);
}
}
};
}
/**
* Gets the outputs to use with advice with from the given child/parent
* <p/>
* This implementation deals with that outputs can be abstract and retrieves the <i>correct</i> parent output.
*
* @param node the node
* @return <tt>null</tt> if not outputs to be used
*/
private static List<ProcessorDefinition<?>> getOutputs(ProcessorDefinition<?> node) {
if (node == null) {
return null;
}
ProcessorDefinition<?> parent = node.getParent();
if (parent == null) {
return null;
}
// for CBR then use the outputs from the node itself
// so we work on the right branch in the CBR (when/otherwise)
if (parent instanceof ChoiceDefinition) {
return node.getOutputs();
}
List<ProcessorDefinition<?>> outputs = parent.getOutputs();
if (outputs.size() == 1 && outputs.get(0).isAbstract()) {
// if the output is abstract then get its output, as
outputs = outputs.get(0).getOutputs();
}
return outputs;
}
public static AdviceWithTask replaceFromWith(final RouteDefinition route, final String uri) {
return new AdviceWithTask() {
public void task() throws Exception {
FromDefinition from = route.getInputs().get(0);
LOG.info("AdviceWith replace input from [{}] --> [{}]", from.getUriOrRef(), uri);
from.setEndpoint(null);
from.setRef(null);
from.setUri(uri);
}
};
}
public static AdviceWithTask replaceFrom(final RouteDefinition route, final Endpoint endpoint) {
return new AdviceWithTask() {
public void task() throws Exception {
FromDefinition from = route.getInputs().get(0);
LOG.info("AdviceWith replace input from [{}] --> [{}]", from.getUriOrRef(), endpoint.getEndpointUri());
from.setRef(null);
from.setUri(null);
from.setEndpoint(endpoint);
}
};
}
/**
* Create iterator which walks the route, and only returns nodes which matches the given set of criteria.
*
* @param route the route
* @param matchBy match by which must match
* @param selectFirst optional to select only the first
* @param selectLast optional to select only the last
* @param selectFrom optional to select index/range
* @param selectTo optional to select index/range
* @param maxDeep maximum levels deep (is unbounded by default)
*
* @return the iterator
*/
private static Iterator<ProcessorDefinition<?>> createMatchByIterator(final RouteDefinition route, final MatchBy matchBy,
final boolean selectFirst, final boolean selectLast,
final int selectFrom, final int selectTo, int maxDeep) {
// first iterator and apply match by
List<ProcessorDefinition<?>> matched = new ArrayList<ProcessorDefinition<?>>();
@SuppressWarnings("rawtypes")
Iterator<ProcessorDefinition> itAll = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class, maxDeep);
while (itAll.hasNext()) {
ProcessorDefinition<?> next = itAll.next();
if (matchBy.match(next)) {
matched.add(next);
}
}
// and then apply the selector iterator
return createSelectorIterator(matched, selectFirst, selectLast, selectFrom, selectTo);
}
private static Iterator<ProcessorDefinition<?>> createSelectorIterator(final List<ProcessorDefinition<?>> list, final boolean selectFirst,
final boolean selectLast, final int selectFrom, final int selectTo) {
return new Iterator<ProcessorDefinition<?>>() {
private int current;
private boolean done;
@Override
public boolean hasNext() {
if (list.isEmpty() || done) {
return false;
}
if (selectFirst) {
done = true;
// spool to first
current = 0;
return true;
}
if (selectLast) {
done = true;
// spool to last
current = list.size() - 1;
return true;
}
if (selectFrom >= 0 && selectTo >= 0) {
// check for out of bounds
if (selectFrom >= list.size() || selectTo >= list.size()) {
return false;
}
if (current < selectFrom) {
// spool to beginning of range
current = selectFrom;
}
return current >= selectFrom && current <= selectTo;
}
return current < list.size();
}
@Override
public ProcessorDefinition<?> next() {
ProcessorDefinition<?> answer = list.get(current);
current++;
return answer;
}
@Override
public void remove() {
// noop
}
};
}
}