This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch on-header in repository https://gitbox.apache.org/repos/asf/camel.git
commit 63c4df47ae1908e52e091643d32cd46a45aee652 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Thu Feb 4 16:04:00 2021 +0100 CAMEL-16102: Source code generate @InvokeOnHeader for reflection free --- .../java/org/apache/camel/spi/InvokeOnHeader.java | 17 +-- .../impl/engine/DefaultUriFactoryResolver.java | 7 +- .../apache/camel/support/BaseSelectorProducer.java | 56 ---------- .../camel/support/HeaderSelectorProducer.java | 118 ++++++++++----------- 4 files changed, 70 insertions(+), 128 deletions(-) diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/InvokeOnHeader.java b/core/camel-api/src/main/java/org/apache/camel/spi/InvokeOnHeader.java index fc0e977..0dc11e9 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/InvokeOnHeader.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/InvokeOnHeader.java @@ -27,15 +27,18 @@ import org.apache.camel.Message; /** * Marks a method as being invoked for a specific header value. * <p/> - * The method must have either of the following method signatures: + * The method must have either of the following method signatures: + * * <pre> - * void theMethodName(Message message) throws Exception; - * Object theMethodName(Message message) throws Exception; - * boolean theMethodName(Message message, AsyncCallback callback) throws Exception; + * void theMethodName(Message message) throws Exception; + * + * Object theMethodName(Message message) throws Exception; + * + * boolean theMethodName(Message message, AsyncCallback callback) throws Exception; * </pre> - * If the method includes the {@link AsyncCallback} type, then the return value must be boolean, - * as part of the async callback contract. - * Throwing exceptions is optional and can be omitted. + * + * If the method includes the {@link AsyncCallback} type, then the return value must be boolean, as part of the async + * callback contract. Throwing exceptions is optional and can be omitted. * <p/> * This can be used by Component implementations that uses org.apache.camel.support.HeaderSelectorProducer. * diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUriFactoryResolver.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUriFactoryResolver.java index acfb865..0558608 100644 --- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUriFactoryResolver.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUriFactoryResolver.java @@ -31,10 +31,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Default assembler resolver that looks for assembler factories in + * Default assembler resolver that looks for {@link UriFactoryResolver} factories in * <b>META-INF/services/org/apache/camel/urifactory/</b>. */ - public class DefaultUriFactoryResolver implements CamelContextAware, UriFactoryResolver { public static final String RESOURCE_PATH = "META-INF/services/org/apache/camel/urifactory/"; @@ -70,7 +69,7 @@ public class DefaultUriFactoryResolver implements CamelContextAware, UriFactoryR // not in registry then use assembler factory for endpoints Class<?> type; try { - type = findAssembler(name + "-endpoint", context); + type = findFactory(name + "-endpoint", context); } catch (NoFactoryAvailableException e) { // its optional so its okay type = null; @@ -99,7 +98,7 @@ public class DefaultUriFactoryResolver implements CamelContextAware, UriFactoryR return answer; } - private Class<?> findAssembler(String name, CamelContext context) throws IOException { + private Class<?> findFactory(String name, CamelContext context) throws IOException { if (factoryFinder == null) { factoryFinder = context.adapt(ExtendedCamelContext.class).getFactoryFinder(RESOURCE_PATH); } diff --git a/core/camel-support/src/main/java/org/apache/camel/support/BaseSelectorProducer.java b/core/camel-support/src/main/java/org/apache/camel/support/BaseSelectorProducer.java deleted file mode 100644 index 8ab96b2..0000000 --- a/core/camel-support/src/main/java/org/apache/camel/support/BaseSelectorProducer.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.support; - -import org.apache.camel.Endpoint; -import org.apache.camel.Exchange; -import org.apache.camel.Processor; - -/** - * A base class for selector-based producers. - */ -public abstract class BaseSelectorProducer extends DefaultProducer { - - protected BaseSelectorProducer(Endpoint endpoint) { - super(endpoint); - } - - @Override - public void process(Exchange exchange) throws Exception { - final Processor processor = getProcessor(exchange); - if (processor != null) { - processor.process(exchange); - } else { - onMissingProcessor(exchange); - } - } - - /** - * Determine the processor to use to handle the exchange. - * - * @param exchange the message exchange - * @return the processor to processes the message exchange - */ - protected abstract Processor getProcessor(Exchange exchange) throws Exception; - - /** - * Invoked when no processor has been defined to process the message exchange. - * - * @param exchange the message exchange - */ - protected abstract void onMissingProcessor(Exchange exchange) throws Exception; -} diff --git a/core/camel-support/src/main/java/org/apache/camel/support/HeaderSelectorProducer.java b/core/camel-support/src/main/java/org/apache/camel/support/HeaderSelectorProducer.java index fbcb0c8..1aa2b49 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/HeaderSelectorProducer.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/HeaderSelectorProducer.java @@ -16,35 +16,34 @@ */ package org.apache.camel.support; -import java.lang.reflect.Method; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.TreeMap; import java.util.function.Supplier; +import org.apache.camel.AsyncCallback; +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; -import org.apache.camel.Message; +import org.apache.camel.ExtendedCamelContext; import org.apache.camel.NoSuchHeaderException; -import org.apache.camel.Processor; -import org.apache.camel.spi.InvokeOnHeader; +import org.apache.camel.spi.InvokeOnHeaderStrategy; import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.apache.camel.support.ObjectHelper.invokeMethodSafe; - /** * A selector-based producer which uses a header value to determine which processor should be invoked. */ -public abstract class HeaderSelectorProducer extends BaseSelectorProducer { +public abstract class HeaderSelectorProducer extends DefaultAsyncProducer implements CamelContextAware { + + public static final String RESOURCE_PATH = "META-INF/services/org/apache/camel/invoke-on-header/"; + private static final Logger LOGGER = LoggerFactory.getLogger(HeaderSelectorProducer.class); private final Supplier<String> headerSupplier; private final Supplier<String> defaultHeaderValueSupplier; private final Object target; - private Map<String, Processor> handlers; + private CamelContext camelContext; + private InvokeOnHeaderStrategy strategy; public HeaderSelectorProducer(Endpoint endpoint, Supplier<String> headerSupplier) { this(endpoint, headerSupplier, () -> null, null); @@ -126,71 +125,68 @@ public abstract class HeaderSelectorProducer extends BaseSelectorProducer { this.headerSupplier = ObjectHelper.notNull(headerSupplier, "headerSupplier"); this.defaultHeaderValueSupplier = ObjectHelper.notNull(defaultHeaderValueSupplier, "defaultHeaderValueSupplier"); this.target = target != null ? target : this; - this.handlers = caseSensitive ? new HashMap<>() : new TreeMap<>(String.CASE_INSENSITIVE_ORDER); } @Override - protected void doStart() throws Exception { - bind(); - - handlers = Collections.unmodifiableMap(handlers); - - super.doStart(); + public CamelContext getCamelContext() { + return camelContext; } @Override - protected Processor getProcessor(Exchange exchange) throws Exception { - String header = headerSupplier.get(); - String action = exchange.getIn().getHeader(header, String.class); - - if (action == null) { - action = defaultHeaderValueSupplier.get(); - } - if (action == null) { - throw new NoSuchHeaderException(exchange, header, String.class); - } - - return handlers.get(action); + public void setCamelContext(CamelContext camelContext) { + this.camelContext = camelContext; } @Override - protected void onMissingProcessor(Exchange exchange) throws Exception { - throw new IllegalStateException( - "Unsupported operation " + exchange.getIn().getHeader(headerSupplier.get())); - } + protected void doBuild() throws Exception { + super.doBuild(); - // TODO: bind should use factory finder and use reflection free - // TODO: And setup this as part of doBuild as its loaded via classpath so we can build time optimize this - protected void bind() { - for (final Method method : getTarget().getClass().getDeclaredMethods()) { - bind(method.getAnnotation(InvokeOnHeader.class), method); - } - } - - protected final void bind(String key, Processor processor) { - if (handlers.containsKey(key)) { - LOGGER.warn("A processor is already set for action {}", key); - } - - this.handlers.put(key, processor); + String key = this.getClass().getName(); + String fqn = RESOURCE_PATH + "/" + key; + strategy = camelContext.adapt(ExtendedCamelContext.class).getBootstrapFactoryFinder(RESOURCE_PATH) + .newInstance(key, InvokeOnHeaderStrategy.class) + .orElseThrow(() -> new IllegalArgumentException("Cannot find " + fqn + " in classpath.")); } - protected void bind(InvokeOnHeader handler, final Method method) { - if (handler != null && method.getParameterCount() == 1) { - final Class<?> type = method.getParameterTypes()[0]; + @Override + public boolean process(Exchange exchange, AsyncCallback callback) { + try { + String header = headerSupplier.get(); + String action = exchange.getIn().getHeader(header, String.class); - LOGGER.debug("bind key={}, class={}, method={}, type={}", - handler.value(), this.getClass(), method.getName(), type); + if (action == null) { + action = defaultHeaderValueSupplier.get(); + } + if (action == null) { + throw new NoSuchHeaderException(exchange, header, String.class); + } - if (Message.class.isAssignableFrom(type)) { - bind(handler.value(), e -> invokeMethodSafe(method, target, e.getIn())); - } else { - bind(handler.value(), e -> invokeMethodSafe(method, target, e)); + LOGGER.debug("Invoking @InvokeOnHeader method: {}", action); + Object answer = strategy.invoke(this, action, exchange, callback); + LOGGER.trace("Invoked @InvokeOnHeader method: {} -> {}", action, answer); + + if (answer == null) { + // strategy invoked synchronously so trigger callback and return true + callback.done(true); + return true; + } else if (answer instanceof Boolean) { + boolean bool = (boolean) answer; + if (bool) { + // strategy invoked synchronously so trigger callback and return true + callback.done(true); + return true; + } else { + // strategy is invoking this asynchronously so return false + return false; + } } + } catch (Exception e) { + exchange.setException(e); + callback.done(true); + return true; } - } - protected final Object getTarget() { - return this; + return false; } + }