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.activemq.camel.component;
018
019import java.net.URISyntaxException;
020import java.util.Map;
021import java.util.concurrent.CopyOnWriteArrayList;
022
023import javax.jms.Connection;
024
025import org.apache.activemq.EnhancedConnection;
026import org.apache.activemq.Service;
027import org.apache.activemq.advisory.DestinationSource;
028import org.apache.camel.CamelContext;
029import org.apache.camel.component.jms.JmsComponent;
030import org.apache.camel.component.jms.JmsConfiguration;
031import org.apache.camel.util.IntrospectionSupport;
032import org.apache.camel.util.ObjectHelper;
033import org.apache.camel.util.URISupport;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036import org.springframework.jms.connection.SingleConnectionFactory;
037import org.springframework.jms.core.JmsTemplate;
038
039/**
040 * The <a href="http://activemq.apache.org/camel/activemq.html">ActiveMQ Component</a>
041 */
042public class ActiveMQComponent extends JmsComponent {
043    private final CopyOnWriteArrayList<SingleConnectionFactory> singleConnectionFactoryList =
044            new CopyOnWriteArrayList<SingleConnectionFactory>();
045    private final CopyOnWriteArrayList<Service> pooledConnectionFactoryServiceList =
046            new CopyOnWriteArrayList<Service>();
047    private static final transient Logger LOG = LoggerFactory.getLogger(ActiveMQComponent.class);
048    private boolean exposeAllQueues;
049    private CamelEndpointLoader endpointLoader;
050
051    private EnhancedConnection connection;
052    DestinationSource source;
053
054    /**
055     * Creates an <a href="http://camel.apache.org/activemq.html">ActiveMQ Component</a>
056     *
057     * @return the created component
058     */
059    public static ActiveMQComponent activeMQComponent() {
060        return new ActiveMQComponent();
061    }
062
063    /**
064     * Creates an <a href="http://camel.apache.org/activemq.html">ActiveMQ Component</a>
065     * connecting to the given <a href="http://activemq.apache.org/configuring-transports.html">broker URL</a>
066     *
067     * @param brokerURL the URL to connect to
068     * @return the created component
069     */
070    public static ActiveMQComponent activeMQComponent(String brokerURL) {
071        ActiveMQComponent answer = new ActiveMQComponent();
072        if (answer.getConfiguration() instanceof ActiveMQConfiguration) {
073            ((ActiveMQConfiguration) answer.getConfiguration())
074                    .setBrokerURL(brokerURL);
075        }
076
077        return answer;
078    }
079
080    public ActiveMQComponent() {
081    }
082
083    public ActiveMQComponent(CamelContext context) {
084        super(context);
085    }
086
087    public ActiveMQComponent(ActiveMQConfiguration configuration) {
088        super();
089        setConfiguration(configuration);
090    }
091
092    /**
093     * Sets the broker URL to use to connect to ActiveMQ using the
094     * <a href="http://activemq.apache.org/configuring-transports.html">ActiveMQ URI format</a>
095     */
096    public void setBrokerURL(String brokerURL) {
097        if (getConfiguration() instanceof ActiveMQConfiguration) {
098            ((ActiveMQConfiguration)getConfiguration()).setBrokerURL(brokerURL);
099        }
100    }
101
102    public void setTrustAllPackages(boolean trustAllPackages) {
103        if (getConfiguration() instanceof ActiveMQConfiguration) {
104            ((ActiveMQConfiguration)getConfiguration()).setTrustAllPackages(trustAllPackages);
105        }
106    }
107
108    public boolean isExposeAllQueues() {
109        return exposeAllQueues;
110    }
111
112    /**
113     * If enabled this will cause all Queues in the ActiveMQ broker to be eagerly populated into the CamelContext
114     * so that they can be easily browsed by any Camel tooling. This option is disabled by default.
115     */
116    public void setExposeAllQueues(boolean exposeAllQueues) {
117        this.exposeAllQueues = exposeAllQueues;
118    }
119
120    /**
121     * Enables or disables whether a PooledConnectionFactory will be used so that when
122     * messages are sent to ActiveMQ from outside of a message consuming thread, pooling will be used rather
123     * than the default with the Spring {@link JmsTemplate} which will create a new connection, session, producer
124     * for each message then close them all down again.
125     * <p/>
126     * The default value is true. Note that this requires an extra dependency on commons-pool2.
127     */
128    public void setUsePooledConnection(boolean usePooledConnection) {
129        if (getConfiguration() instanceof ActiveMQConfiguration) {
130            ((ActiveMQConfiguration)getConfiguration()).setUsePooledConnection(usePooledConnection);
131        }
132    }
133
134    /**
135     * Enables or disables whether a Spring {@link SingleConnectionFactory} will be used so that when
136     * messages are sent to ActiveMQ from outside of a message consuming thread, pooling will be used rather
137     * than the default with the Spring {@link JmsTemplate} which will create a new connection, session, producer
138     * for each message then close them all down again.
139     * <p/>
140     * The default value is false and a pooled connection is used by default.
141     */
142    public void setUseSingleConnection(boolean useSingleConnection) {
143        if (getConfiguration() instanceof ActiveMQConfiguration) {
144            ((ActiveMQConfiguration)getConfiguration()).setUseSingleConnection(useSingleConnection);
145        }
146    }
147
148    protected void addPooledConnectionFactoryService(Service pooledConnectionFactoryService) {
149        pooledConnectionFactoryServiceList.add(pooledConnectionFactoryService);
150    }
151
152    protected void addSingleConnectionFactory(SingleConnectionFactory singleConnectionFactory) {
153        singleConnectionFactoryList.add(singleConnectionFactory);
154    }
155
156    @Override
157    @SuppressWarnings("unchecked")
158    protected String convertPathToActualDestination(String path, Map<String, Object> parameters) {
159        // support ActiveMQ destination options using the destination. prefix
160        // http://activemq.apache.org/destination-options.html
161        Map options = IntrospectionSupport.extractProperties(parameters, "destination.");
162
163        String query;
164        try {
165            query = URISupport.createQueryString(options);
166        } catch (URISyntaxException e) {
167            throw ObjectHelper.wrapRuntimeCamelException(e);
168        }
169
170        // if we have destination options then append them to the destination name
171        if (ObjectHelper.isNotEmpty(query)) {
172            return path + "?" + query;
173        } else {
174            return path;
175        }
176    }
177
178    @Override
179    protected void doStart() throws Exception {
180        super.doStart();
181
182        if (isExposeAllQueues()) {
183            createDestinationSource();
184            endpointLoader = new CamelEndpointLoader(getCamelContext(), source);
185            endpointLoader.afterPropertiesSet();
186        }
187
188        // use OriginalDestinationPropagateStrategy by default if no custom stategy has been set
189        if (getMessageCreatedStrategy() == null) {
190            setMessageCreatedStrategy(new OriginalDestinationPropagateStrategy());
191        }
192    }
193
194    protected void createDestinationSource() {
195        try {
196            if (source == null) {
197                if (connection == null) {
198                    Connection value = getConfiguration().getConnectionFactory().createConnection();
199                    if (value instanceof EnhancedConnection) {
200                        connection = (EnhancedConnection) value;
201                    } else {
202                        throw new IllegalArgumentException("Created JMS Connection is not an EnhancedConnection: " + value);
203                    }
204                    connection.start();
205                }
206                source = connection.getDestinationSource();
207            }
208        } catch (Throwable t) {
209            LOG.info("Can't get destination source, endpoint completer will not work", t);
210        }
211    }
212
213    @Override
214    protected void doStop() throws Exception {
215        if (source != null) {
216            source.stop();
217            source = null;
218        }
219        if (connection != null) {
220            connection.close();
221            connection = null;
222        }
223        for (Service s : pooledConnectionFactoryServiceList) {
224            s.stop();
225        }
226        pooledConnectionFactoryServiceList.clear();
227        for (SingleConnectionFactory s : singleConnectionFactoryList) {
228            s.destroy();
229        }
230        singleConnectionFactoryList.clear();
231        super.doStop();
232    }
233
234    @Override
235    public void setConfiguration(JmsConfiguration configuration) {
236        if (configuration instanceof ActiveMQConfiguration) {
237            ((ActiveMQConfiguration) configuration).setActiveMQComponent(this);
238        }
239        super.setConfiguration(configuration);
240    }
241
242    @Override
243    protected JmsConfiguration createConfiguration() {
244        ActiveMQConfiguration answer = new ActiveMQConfiguration();
245        answer.setActiveMQComponent(this);
246        return answer;
247    }
248
249}