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