All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.component.sjms.batch.SjmsBatchEndpoint Maven / Gradle / Ivy

There is a newer version: 4.9.0
Show newest version
/*
 * 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.component.sjms.batch;

import java.util.concurrent.ScheduledExecutorService;

import javax.jms.Message;
import javax.jms.Session;

import org.apache.camel.AggregationStrategy;
import org.apache.camel.Component;
import org.apache.camel.Consumer;
import org.apache.camel.Exchange;
import org.apache.camel.Predicate;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.component.sjms.SjmsHeaderFilterStrategy;
import org.apache.camel.component.sjms.SjmsMessage;
import org.apache.camel.component.sjms.jms.DefaultJmsKeyFormatStrategy;
import org.apache.camel.component.sjms.jms.DestinationNameParser;
import org.apache.camel.component.sjms.jms.JmsBinding;
import org.apache.camel.component.sjms.jms.JmsKeyFormatStrategy;
import org.apache.camel.component.sjms.jms.MessageCreatedStrategy;
import org.apache.camel.spi.HeaderFilterStrategy;
import org.apache.camel.spi.HeaderFilterStrategyAware;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.spi.UriParam;
import org.apache.camel.spi.UriPath;
import org.apache.camel.support.DefaultEndpoint;

/**
 * Highly performant and transactional batch consumption of messages from a JMS queue.
 */
@UriEndpoint(firstVersion = "2.16.0", scheme = "sjms-batch", title = "Simple JMS Batch", syntax = "sjms-batch:destinationName",
        label = "messaging", consumerOnly = true)
public class SjmsBatchEndpoint extends DefaultEndpoint implements HeaderFilterStrategyAware {

    public static final int DEFAULT_COMPLETION_SIZE = 200; // the default dispatch queue size in ActiveMQ
    public static final int DEFAULT_COMPLETION_TIMEOUT = 500;

    private JmsBinding binding;

    @UriPath @Metadata(required = true)
    private String destinationName;
    @UriParam @Metadata(required = true)
    private AggregationStrategy aggregationStrategy;
    @UriParam(defaultValue = "1")
    private int consumerCount = 1;
    @UriParam(defaultValue = "200")
    private int completionSize = DEFAULT_COMPLETION_SIZE;
    @UriParam(defaultValue = "500")
    private int completionTimeout = DEFAULT_COMPLETION_TIMEOUT;
    @UriParam(defaultValue = "1000")
    private int completionInterval;
    @UriParam(javaType = "java.lang.String")
    private Predicate completionPredicate;
    @UriParam
    private boolean eagerCheckCompletion;
    @UriParam
    private boolean sendEmptyMessageWhenIdle;
    @UriParam(defaultValue = "1000")
    private int pollDuration = 1000;
    @UriParam
    private boolean includeAllJMSXProperties;
    @UriParam(defaultValue = "true")
    private boolean allowNullBody = true;
    @UriParam(defaultValue = "true")
    private boolean mapJmsMessage = true;
    @UriParam(label = "advanced")
    private HeaderFilterStrategy headerFilterStrategy;
    @UriParam(label = "advanced")
    private MessageCreatedStrategy messageCreatedStrategy;
    @UriParam(label = "advanced")
    private JmsKeyFormatStrategy jmsKeyFormatStrategy;
    @UriParam(label = "advanced")
    private ScheduledExecutorService timeoutCheckerExecutorService;
    @UriParam(label = "advanced")
    private boolean asyncStartListener;
    @UriParam(label = "advanced", defaultValue = "5000")
    private int recoveryInterval = 5000;
    @UriParam(label = "advanced", defaultValue = "-1")
    private int keepAliveDelay = -1;

    public SjmsBatchEndpoint() {
    }

    public SjmsBatchEndpoint(String endpointUri, Component component, String remaining) {
        super(endpointUri, component);

        DestinationNameParser parser = new DestinationNameParser();
        if (parser.isTopic(remaining)) {
            throw new IllegalArgumentException("Only batch consumption from queues is supported. For topics you "
                    + "should use a regular JMS consumer with an aggregator.");
        }
        this.destinationName = parser.getShortName(remaining);
    }

    @Override
    public SjmsBatchComponent getComponent() {
        return (SjmsBatchComponent) super.getComponent();
    }

    @Override
    public Producer createProducer() throws Exception {
        throw new UnsupportedOperationException("Producer not supported");
    }

    @Override
    public Consumer createConsumer(Processor processor) throws Exception {
        SjmsBatchConsumer consumer = new SjmsBatchConsumer(this, processor);
        consumer.setTimeoutCheckerExecutorService(timeoutCheckerExecutorService);
        configureConsumer(consumer);
        return consumer;
    }

    public Exchange createExchange(Message message, Session session) {
        Exchange exchange = createExchange(getExchangePattern());
        exchange.setIn(new SjmsMessage(exchange, message, session, getBinding()));
        return exchange;
    }

    public JmsBinding getBinding() {
        if (binding == null) {
            binding = createBinding();
        }
        return binding;
    }

    /**
     * Creates the {@link org.apache.camel.component.sjms.jms.JmsBinding} to use.
     */
    protected JmsBinding createBinding() {
        return new JmsBinding(isMapJmsMessage(), isAllowNullBody(), getHeaderFilterStrategy(), getJmsKeyFormatStrategy(), getMessageCreatedStrategy());
    }

    /**
     * Sets the binding used to convert from a Camel message to and from a JMS
     * message
     */
    public void setBinding(JmsBinding binding) {
        this.binding = binding;
    }

    public AggregationStrategy getAggregationStrategy() {
        return aggregationStrategy;
    }

    /**
     * The aggregation strategy to use, which merges all the batched messages into a single message
     */
    public void setAggregationStrategy(AggregationStrategy aggregationStrategy) {
        this.aggregationStrategy = aggregationStrategy;
    }

    /**
     * The destination name. Only queues are supported, names may be prefixed by 'queue:'.
     */
    public String getDestinationName() {
        return destinationName;
    }

    public int getConsumerCount() {
        return consumerCount;
    }

    /**
     * The number of JMS sessions to consume from
     */
    public void setConsumerCount(int consumerCount) {
        this.consumerCount = consumerCount;
    }

    public int getCompletionSize() {
        return completionSize;
    }

    /**
     * The number of messages consumed at which the batch will be completed
     */
    public void setCompletionSize(int completionSize) {
        this.completionSize = completionSize;
    }

    public int getCompletionTimeout() {
        return completionTimeout;
    }

    /**
     * The timeout in millis from receipt of the first first message when the batch will be completed.
     * The batch may be empty if the timeout triggered and there was no messages in the batch.
     * 
* Notice you cannot use both completion timeout and completion interval at the same time, only one can be configured. */ public void setCompletionTimeout(int completionTimeout) { this.completionTimeout = completionTimeout; } public int getCompletionInterval() { return completionInterval; } /** * The completion interval in millis, which causes batches to be completed in a scheduled fixed rate every interval. * The batch may be empty if the timeout triggered and there was no messages in the batch. *
* Notice you cannot use both completion timeout and completion interval at the same time, only one can be configured. */ public void setCompletionInterval(int completionInterval) { this.completionInterval = completionInterval; } public Predicate getCompletionPredicate() { return completionPredicate; } /** * The completion predicate, which causes batches to be completed when the predicate evaluates as true. *

* The predicate can also be configured using the simple language using the string syntax. * You may want to set the option eagerCheckCompletion to true to let the predicate match the incoming message, * as otherwise it matches the aggregated message. */ public void setCompletionPredicate(Predicate completionPredicate) { this.completionPredicate = completionPredicate; } public void setCompletionPredicate(String predicate) { // uses simple language this.completionPredicate = getCamelContext().resolveLanguage("simple").createPredicate(predicate); } public boolean isEagerCheckCompletion() { return eagerCheckCompletion; } /** * Use eager completion checking which means that the completionPredicate will use the incoming Exchange. * As opposed to without eager completion checking the completionPredicate will use the aggregated Exchange. */ public void setEagerCheckCompletion(boolean eagerCheckCompletion) { this.eagerCheckCompletion = eagerCheckCompletion; } public boolean isSendEmptyMessageWhenIdle() { return sendEmptyMessageWhenIdle; } /** * If using completion timeout or interval, then the batch may be empty if the timeout triggered and there was no messages in the batch. * If this option is true and the batch is empty then an empty message is added to the batch so an empty message is routed. */ public void setSendEmptyMessageWhenIdle(boolean sendEmptyMessageWhenIdle) { this.sendEmptyMessageWhenIdle = sendEmptyMessageWhenIdle; } public int getPollDuration() { return pollDuration; } /** * The duration in milliseconds of each poll for messages. * completionTimeOut will be used if it is shorter and a batch has started. */ public void setPollDuration(int pollDuration) { this.pollDuration = pollDuration; } public boolean isAllowNullBody() { return allowNullBody; } /** * Whether to allow sending messages with no body. If this option is false and the message body is null, then an JMSException is thrown. */ public void setAllowNullBody(boolean allowNullBody) { this.allowNullBody = allowNullBody; } public boolean isMapJmsMessage() { return mapJmsMessage; } /** * Specifies whether Camel should auto map the received JMS message to a suited payload type, such as javax.jms.TextMessage to a String etc. * See section about how mapping works below for more details. */ public void setMapJmsMessage(boolean mapJmsMessage) { this.mapJmsMessage = mapJmsMessage; } public MessageCreatedStrategy getMessageCreatedStrategy() { return messageCreatedStrategy; } /** * To use the given MessageCreatedStrategy which are invoked when Camel creates new instances of javax.jms.Message * objects when Camel is sending a JMS message. */ public void setMessageCreatedStrategy(MessageCreatedStrategy messageCreatedStrategy) { this.messageCreatedStrategy = messageCreatedStrategy; } public JmsKeyFormatStrategy getJmsKeyFormatStrategy() { if (jmsKeyFormatStrategy == null) { jmsKeyFormatStrategy = new DefaultJmsKeyFormatStrategy(); } return jmsKeyFormatStrategy; } /** * Pluggable strategy for encoding and decoding JMS keys so they can be compliant with the JMS specification. * Camel provides two implementations out of the box: default and passthrough. * The default strategy will safely marshal dots and hyphens (. and -). The passthrough strategy leaves the key as is. * Can be used for JMS brokers which do not care whether JMS header keys contain illegal characters. * You can provide your own implementation of the org.apache.camel.component.jms.JmsKeyFormatStrategy * and refer to it using the # notation. */ public void setJmsKeyFormatStrategy(JmsKeyFormatStrategy jmsKeyFormatStrategy) { this.jmsKeyFormatStrategy = jmsKeyFormatStrategy; } @Override public HeaderFilterStrategy getHeaderFilterStrategy() { if (headerFilterStrategy == null) { headerFilterStrategy = new SjmsHeaderFilterStrategy(isIncludeAllJMSXProperties()); } return headerFilterStrategy; } /** * To use a custom HeaderFilterStrategy to filter header to and from Camel message. */ @Override public void setHeaderFilterStrategy(HeaderFilterStrategy strategy) { this.headerFilterStrategy = strategy; } public boolean isIncludeAllJMSXProperties() { return includeAllJMSXProperties; } /** * Whether to include all JMSXxxx properties when mapping from JMS to Camel Message. * Setting this to true will include properties such as JMSXAppID, and JMSXUserID etc. * Note: If you are using a custom headerFilterStrategy then this option does not apply. */ public void setIncludeAllJMSXProperties(boolean includeAllJMSXProperties) { this.includeAllJMSXProperties = includeAllJMSXProperties; } public ScheduledExecutorService getTimeoutCheckerExecutorService() { return timeoutCheckerExecutorService; } /** * If using the completionInterval option a background thread is created to trigger the completion interval. * Set this option to provide a custom thread pool to be used rather than creating a new thread for every consumer. */ public void setTimeoutCheckerExecutorService(ScheduledExecutorService timeoutCheckerExecutorService) { this.timeoutCheckerExecutorService = timeoutCheckerExecutorService; } public boolean isAsyncStartListener() { return asyncStartListener; } /** * Whether to startup the consumer message listener asynchronously, when starting a route. * For example if a JmsConsumer cannot get a connection to a remote JMS broker, then it may block while retrying * and/or failover. This will cause Camel to block while starting routes. By setting this option to true, * you will let routes startup, while the JmsConsumer connects to the JMS broker using a dedicated thread * in asynchronous mode. If this option is used, then beware that if the connection could not be established, * then an exception is logged at WARN level, and the consumer will not be able to receive messages; * You can then restart the route to retry. */ public void setAsyncStartListener(boolean asyncStartListener) { this.asyncStartListener = asyncStartListener; } public int getRecoveryInterval() { return recoveryInterval; } /** * Specifies the interval between recovery attempts, i.e. when a connection is being refreshed, in milliseconds. * The default is 5000 ms, that is, 5 seconds. */ public void setRecoveryInterval(int recoveryInterval) { this.recoveryInterval = recoveryInterval; } /** * The delay in millis between attempts to re-establish a valid session. * If this is a positive value the SjmsBatchConsumer will attempt to create a new session if it sees an IllegalStateException * during message consumption. This delay value allows you to pause between attempts to prevent spamming the logs. * If this is a negative value (default is -1) then the SjmsBatchConsumer will behave as it always has before - that is * it will bail out and the route will shut down if it sees an IllegalStateException. */ public void setKeepAliveDelay(int keepAliveDelay) { this.keepAliveDelay = keepAliveDelay; } public int getKeepAliveDelay() { return keepAliveDelay; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy