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.impl;
018
019import java.lang.reflect.Method;
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.Map;
023import java.util.function.Supplier;
024
025import org.apache.camel.Endpoint;
026import org.apache.camel.Exchange;
027import org.apache.camel.InvokeOnHeader;
028import org.apache.camel.InvokeOnHeaders;
029import org.apache.camel.Message;
030import org.apache.camel.NoSuchHeaderException;
031import org.apache.camel.Processor;
032import org.apache.camel.util.ObjectHelper;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036/**
037 * A selector-based produced which uses an header value to determine which processor
038 * should be invoked.
039 */
040public class HeaderSelectorProducer extends BaseSelectorProducer {
041    private static final Logger LOGGER = LoggerFactory.getLogger(HeaderSelectorProducer.class);
042
043    private final Supplier<String> headerSupplier;
044    private final Supplier<String> defaultHeaderValueSupplier;
045    private final Object target;
046    private Map<String, Processor> handlers;
047
048    public HeaderSelectorProducer(Endpoint endpoint, Supplier<String> headerSupplier) {
049        this(endpoint, headerSupplier, () -> null, null);
050    }
051
052    public HeaderSelectorProducer(Endpoint endpoint, String header) {
053        this(endpoint, () -> header, () -> null, null);
054    }
055
056    public HeaderSelectorProducer(Endpoint endpoint, String header, Object target) {
057        this(endpoint, () -> header, () -> null, target);
058    }
059
060    public HeaderSelectorProducer(Endpoint endpoint,  Supplier<String> headerSupplier, Object target) {
061        this(endpoint, headerSupplier, () -> null, target);
062    }
063
064    public HeaderSelectorProducer(Endpoint endpoint, String header, String defaultHeaderValue) {
065        this(endpoint, () -> header, () -> defaultHeaderValue, null);
066    }
067
068    public HeaderSelectorProducer(Endpoint endpoint, String header, Supplier<String> defaultHeaderValueSupplier) {
069        this(endpoint, () -> header, defaultHeaderValueSupplier, null);
070    }
071
072    public HeaderSelectorProducer(Endpoint endpoint, Supplier<String> headerSupplier, Supplier<String> defaultHeaderValueSupplier) {
073        this(endpoint, headerSupplier, defaultHeaderValueSupplier, null);
074    }
075
076    public HeaderSelectorProducer(Endpoint endpoint, String header, String defaultHeaderValue, Object target) {
077        this(endpoint, () -> header, () -> defaultHeaderValue, target);
078    }
079
080    public HeaderSelectorProducer(Endpoint endpoint, Supplier<String> headerSupplier, Supplier<String> defaultHeaderValueSupplier, Object target) {
081        super(endpoint);
082
083        this.headerSupplier = ObjectHelper.notNull(headerSupplier, "headerSupplier");
084        this.defaultHeaderValueSupplier = ObjectHelper.notNull(defaultHeaderValueSupplier, "defaultHeaderValueSupplier");
085        this.target = target != null ? target : this;
086        this.handlers = new HashMap<>();
087    }
088
089    @Override
090    protected void doStart() throws Exception {
091        for (final Method method : target.getClass().getDeclaredMethods()) {
092            InvokeOnHeaders annotation = method.getAnnotation(InvokeOnHeaders.class);
093            if (annotation != null) {
094                for (InvokeOnHeader processor : annotation.value()) {
095                    bind(processor, method);
096                }
097            } else {
098                bind(method.getAnnotation(InvokeOnHeader.class), method);
099            }
100        }
101
102        handlers = Collections.unmodifiableMap(handlers);
103
104        super.doStart();
105    }
106
107    @Override
108    protected Processor getProcessor(Exchange exchange) throws Exception {
109        String header = headerSupplier.get();
110        String action = exchange.getIn().getHeader(header, String.class);
111
112        if (action == null) {
113            action = defaultHeaderValueSupplier.get();
114        }
115        if (action == null) {
116            throw new NoSuchHeaderException(exchange, header, String.class);
117        }
118
119        return handlers.get(action);
120    }
121
122    protected void onMissingProcessor(Exchange exchange) throws Exception {
123        throw new IllegalStateException(
124            "Unsupported operation " + exchange.getIn().getHeader(headerSupplier.get())
125        );
126    }
127
128    protected final void bind(String key, Processor processor) {
129        if (handlers.containsKey(key)) {
130            LOGGER.warn("A processor is already set for action {}", key);
131        }
132
133        this.handlers.put(key, processor);
134    }
135
136    private void bind(InvokeOnHeader handler, final Method method) {
137        if (handler != null && method.getParameterCount() == 1) {
138            method.setAccessible(true);
139
140            final Class<?> type = method.getParameterTypes()[0];
141
142            LOGGER.debug("bind key={}, class={}, method={}, type={}",
143                handler.value(), this.getClass(), method.getName(), type);
144
145            if (Message.class.isAssignableFrom(type)) {
146                bind(handler.value(), e -> method.invoke(target, e.getIn()));
147            } else {
148                bind(handler.value(), e -> method.invoke(target, e));
149            }
150        }
151    }
152}