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.HashMap;
020import java.util.Map;
021import java.util.Optional;
022import java.util.concurrent.Future;
023import java.util.function.Consumer;
024import java.util.function.Supplier;
025
026import org.apache.camel.CamelContext;
027import org.apache.camel.CamelExecutionException;
028import org.apache.camel.Endpoint;
029import org.apache.camel.Exchange;
030import org.apache.camel.ExchangePattern;
031import org.apache.camel.FluentProducerTemplate;
032import org.apache.camel.Message;
033import org.apache.camel.Processor;
034import org.apache.camel.ProducerTemplate;
035import org.apache.camel.processor.ConvertBodyProcessor;
036import org.apache.camel.support.ServiceSupport;
037import org.apache.camel.util.ExchangeHelper;
038import org.apache.camel.util.ObjectHelper;
039import org.apache.camel.util.ServiceHelper;
040
041public class DefaultFluentProducerTemplate extends ServiceSupport implements FluentProducerTemplate {
042    private final CamelContext context;
043    private final ClassValue<ConvertBodyProcessor> resultProcessors;
044    private Map<String, Object> headers;
045    private Object body;
046    private Optional<Consumer<ProducerTemplate>> templateCustomizer;
047    private Optional<Supplier<Exchange>> exchangeSupplier;
048    private Optional<Supplier<Processor>> processorSupplier;
049    private Optional<Endpoint> endpoint;
050    private Optional<Endpoint> defaultEndpoint;
051    private int maximumCacheSize;
052    private boolean eventNotifierEnabled;
053    private volatile ProducerTemplate template;
054
055    public DefaultFluentProducerTemplate(CamelContext context) {
056        this.context = context;
057        this.endpoint = Optional.empty();
058        this.defaultEndpoint = Optional.empty();
059        this.eventNotifierEnabled = true;
060        this.templateCustomizer = Optional.empty();
061        this.exchangeSupplier = Optional.empty();
062        this.processorSupplier = Optional.empty();
063        this.resultProcessors = new ClassValue<ConvertBodyProcessor>() {
064            @Override
065            protected ConvertBodyProcessor computeValue(Class<?> type) {
066                return new ConvertBodyProcessor(type);
067            }
068        };
069    }
070
071    @Override
072    public CamelContext getCamelContext() {
073        return context;
074    }
075
076    @Override
077    public int getCurrentCacheSize() {
078        if (template == null) {
079            return 0;
080        }
081        return template.getCurrentCacheSize();
082    }
083
084    @Override
085    public void cleanUp() {
086        if (template != null) {
087            template.cleanUp();
088        }
089    }
090
091    @Override
092    public void setDefaultEndpointUri(String endpointUri) {
093        setDefaultEndpoint(getCamelContext().getEndpoint(endpointUri));
094    }
095
096    @Override
097    public Endpoint getDefaultEndpoint() {
098        return defaultEndpoint.orElse(null);
099    }
100
101    @Override
102    public void setDefaultEndpoint(Endpoint defaultEndpoint) {
103        this.defaultEndpoint = Optional.ofNullable(defaultEndpoint);
104    }
105
106    @Override
107    public int getMaximumCacheSize() {
108        return maximumCacheSize;
109    }
110
111    @Override
112    public void setMaximumCacheSize(int maximumCacheSize) {
113        this.maximumCacheSize = maximumCacheSize;
114    }
115
116    @Override
117    public boolean isEventNotifierEnabled() {
118        return eventNotifierEnabled;
119    }
120
121    @Override
122    public void setEventNotifierEnabled(boolean eventNotifierEnabled) {
123        this.eventNotifierEnabled = eventNotifierEnabled;
124    }
125
126    @Override
127    public FluentProducerTemplate clearAll() {
128        clearBody();
129        clearHeaders();
130
131        return this;
132    }
133
134    @Override
135    public FluentProducerTemplate withHeader(String key, Object value) {
136        if (headers == null) {
137            headers = new HashMap<>();
138        }
139
140        headers.put(key, value);
141
142        return this;
143    }
144
145    @Override
146    public FluentProducerTemplate clearHeaders() {
147        if (headers != null) {
148            headers.clear();
149        }
150
151        return this;
152    }
153
154    @Override
155    public FluentProducerTemplate withBody(Object body) {
156        this.body = body;
157
158        return this;
159    }
160
161    @Override
162    public FluentProducerTemplate withBodyAs(Object body, Class<?> type) {
163        this.body = type != null
164            ? context.getTypeConverter().convertTo(type, body)
165            : body;
166
167        return this;
168    }
169
170    @Override
171    public FluentProducerTemplate clearBody() {
172        this.body = null;
173
174        return this;
175    }
176
177    @Override
178    public FluentProducerTemplate withTemplateCustomizer(final Consumer<ProducerTemplate> templateCustomizer) {
179        this.templateCustomizer = Optional.of(templateCustomizer);
180        return this;
181    }
182
183    @Override
184    public FluentProducerTemplate withExchange(final Exchange exchange) {
185        return withExchange(() -> exchange);
186    }
187
188    @Override
189    public FluentProducerTemplate withExchange(final Supplier<Exchange> exchangeSupplier) {
190        this.exchangeSupplier = Optional.of(exchangeSupplier);
191        return this;
192    }
193
194    @Override
195    public FluentProducerTemplate withProcessor(final Processor processor) {
196        return withProcessor(() -> processor);
197    }
198
199    @Override
200    public FluentProducerTemplate withProcessor(final Supplier<Processor> processorSupplier) {
201        this.processorSupplier = Optional.of(processorSupplier);
202        return this;
203    }
204
205    @Override
206    public FluentProducerTemplate to(String endpointUri) {
207        return to(context.getEndpoint(endpointUri));
208    }
209
210    @Override
211    public FluentProducerTemplate to(Endpoint endpoint) {
212        this.endpoint = Optional.of(endpoint);
213        return this;
214    }
215
216    // ************************
217    // REQUEST
218    // ************************
219
220    @Override
221    public Object request() throws CamelExecutionException {
222        return request(Object.class);
223    }
224
225    @Override
226    @SuppressWarnings("unchecked")
227    public <T> T request(Class<T> type) throws CamelExecutionException {
228        // Determine the target endpoint
229        final Endpoint target = target();
230
231        // Create the default processor if not provided.
232        final Supplier<Processor> processorSupplier = this.processorSupplier.orElse(() -> defaultProcessor());
233
234        T result;
235        if (type == Exchange.class) {
236            result = (T)template().request(target, processorSupplier.get());
237        } else if (type == Message.class) {
238            Exchange exchange = template().request(target, processorSupplier.get());
239            result = exchange.hasOut() ? (T)exchange.getOut() : (T)exchange.getIn();
240        } else {
241            Exchange exchange = template().send(
242                target,
243                ExchangePattern.InOut,
244                processorSupplier.get(),
245                resultProcessors.get(type)
246            );
247
248            result = context.getTypeConverter().convertTo(
249                type,
250                ExchangeHelper.extractResultBody(exchange, exchange.getPattern())
251            );
252        }
253
254        return result;
255    }
256
257    @Override
258    public Future<Object> asyncRequest() {
259        return asyncRequest(Object.class);
260    }
261
262    @Override
263    public <T> Future<T> asyncRequest(Class<T> type) {
264        // Determine the target endpoint
265        final Endpoint target = target();
266
267        Future<T> result;
268        if (ObjectHelper.isNotEmpty(headers)) {
269            // Make a copy of the headers and body so that async processing won't
270            // be invalidated by subsequent reuse of the template
271            final Map<String, Object> headersCopy = new HashMap<>(headers);
272            final Object bodyCopy = body;
273
274            result = template().asyncRequestBodyAndHeaders(target, bodyCopy, headersCopy, type);
275        } else {
276            // Make a copy of the and body so that async processing won't be
277            // invalidated by subsequent reuse of the template
278            final Object bodyCopy = body;
279
280            result = template().asyncRequestBody(target, bodyCopy, type);
281        }
282
283        return result;
284    }
285
286    // ************************
287    // SEND
288    // ************************
289
290    @Override
291    public Exchange send() throws CamelExecutionException {
292        // Determine the target endpoint
293        final Endpoint target = target();
294
295        return exchangeSupplier.isPresent()
296            ? template().send(target, exchangeSupplier.get().get())
297            : template().send(target, processorSupplier.orElse(() -> defaultProcessor()).get());
298    }
299
300    @Override
301    public Future<Exchange> asyncSend() {
302        // Determine the target endpoint
303        final Endpoint target = target();
304
305        return exchangeSupplier.isPresent()
306            ? template().asyncSend(target, exchangeSupplier.get().get())
307            : template().asyncSend(target, processorSupplier.orElse(() -> defaultAsyncProcessor()).get());
308    }
309
310    // ************************
311    // HELPERS
312    // ************************
313
314    /**
315     * Create the FluentProducerTemplate by setting the camel context
316     *
317     * @param context the camel context
318     */
319    public static FluentProducerTemplate on(CamelContext context) {
320        return new DefaultFluentProducerTemplate(context);
321    }
322
323    private ProducerTemplate template() {
324        ObjectHelper.notNull(context, "CamelContext");
325
326        if (template == null) {
327            template = maximumCacheSize > 0 ? context.createProducerTemplate(maximumCacheSize) : context.createProducerTemplate();
328            defaultEndpoint.ifPresent(template::setDefaultEndpoint);
329            template.setEventNotifierEnabled(eventNotifierEnabled);
330            templateCustomizer.ifPresent(tc -> tc.accept(template));
331        }
332
333        return template;
334    }
335
336    private Processor defaultProcessor() {
337        return exchange -> {
338            ObjectHelper.ifNotEmpty(headers, exchange.getIn().getHeaders()::putAll);
339            ObjectHelper.ifNotEmpty(body, exchange.getIn()::setBody);
340        };
341    }
342
343    private Processor defaultAsyncProcessor() {
344        final Map<String, Object> headersCopy = ObjectHelper.isNotEmpty(this.headers) ? new HashMap<>(this.headers) : null;
345        final Object bodyCopy = this.body;
346
347        return exchange -> {
348            ObjectHelper.ifNotEmpty(headersCopy, exchange.getIn().getHeaders()::putAll);
349            ObjectHelper.ifNotEmpty(bodyCopy, exchange.getIn()::setBody);
350        };
351    }
352
353    private Endpoint target() {
354        if (endpoint.isPresent()) {
355            return endpoint.get();
356        }
357        if (defaultEndpoint.isPresent()) {
358            return defaultEndpoint.get();
359        }
360
361        throw new IllegalArgumentException("No endpoint configured on FluentProducerTemplate. You can configure an endpoint with to(uri)");
362    }
363
364    @Override
365    protected void doStart() throws Exception {
366        if (template == null) {
367            template = template();
368        }
369        ServiceHelper.startService(template);
370    }
371
372    @Override
373    protected void doStop() throws Exception {
374        ServiceHelper.stopService(template);
375    }
376}