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.*;
021import java.util.concurrent.CopyOnWriteArrayList;
022
023import org.apache.activemq.EnhancedConnection;
024import org.apache.activemq.Service;
025import org.apache.activemq.advisory.DestinationSource;
026import org.apache.activemq.command.ActiveMQDestination;
027import org.apache.camel.CamelContext;
028import org.apache.camel.ComponentConfiguration;
029import org.apache.camel.component.jms.JmsComponent;
030import org.apache.camel.component.jms.JmsConfiguration;
031import org.apache.camel.spi.EndpointCompleter;
032import org.apache.camel.util.IntrospectionSupport;
033import org.apache.camel.util.ObjectHelper;
034import org.apache.camel.util.URISupport;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037import org.springframework.jms.connection.SingleConnectionFactory;
038
039import javax.jms.Connection;
040
041/**
042 * The <a href="http://activemq.apache.org/camel/activemq.html">ActiveMQ Component</a>
043 */
044public class ActiveMQComponent extends JmsComponent implements EndpointCompleter {
045    private final CopyOnWriteArrayList<SingleConnectionFactory> singleConnectionFactoryList =
046        new CopyOnWriteArrayList<SingleConnectionFactory>();
047    private final CopyOnWriteArrayList<Service> pooledConnectionFactoryServiceList =
048        new CopyOnWriteArrayList<Service>();
049    private static final transient Logger LOG = LoggerFactory.getLogger(ActiveMQComponent.class);
050    private boolean exposeAllQueues;
051    private CamelEndpointLoader endpointLoader;
052
053    private EnhancedConnection connection;
054    DestinationSource source;
055    boolean sourceInitialized = false;
056
057    /**
058     * Creates an <a href="http://camel.apache.org/activemq.html">ActiveMQ Component</a>
059     *
060     * @return the created component
061     */
062    public static ActiveMQComponent activeMQComponent() {
063        return new ActiveMQComponent();
064    }
065
066    /**
067     * Creates an <a href="http://camel.apache.org/activemq.html">ActiveMQ Component</a>
068     * connecting to the given <a href="http://activemq.apache.org/configuring-transports.html">broker URL</a>
069     *
070     * @param brokerURL the URL to connect to
071     * @return the created component
072     */
073    public static ActiveMQComponent activeMQComponent(String brokerURL) {
074        ActiveMQComponent answer = new ActiveMQComponent();
075        if (answer.getConfiguration() instanceof ActiveMQConfiguration) {
076            ((ActiveMQConfiguration) answer.getConfiguration())
077                    .setBrokerURL(brokerURL);
078        }
079
080        return answer;
081    }
082
083    public ActiveMQComponent() {
084    }
085
086    public ActiveMQComponent(CamelContext context) {
087        super(context);
088    }
089
090    public ActiveMQComponent(ActiveMQConfiguration configuration) {
091        super();
092        setConfiguration(configuration);
093    }
094
095    public void setBrokerURL(String brokerURL) {
096        if (getConfiguration() instanceof ActiveMQConfiguration) {
097            ((ActiveMQConfiguration)getConfiguration()).setBrokerURL(brokerURL);
098        }
099    }
100
101    public void setUserName(String userName) {
102        if (getConfiguration() instanceof ActiveMQConfiguration) {
103            ((ActiveMQConfiguration)getConfiguration()).setUserName(userName);
104        }
105    }
106
107    public void setPassword(String password) {
108        if (getConfiguration() instanceof ActiveMQConfiguration) {
109            ((ActiveMQConfiguration)getConfiguration()).setPassword(password);
110        }
111    }
112
113    public boolean isExposeAllQueues() {
114        return exposeAllQueues;
115    }
116
117    /**
118     * If enabled this will cause all Queues in the ActiveMQ broker to be eagerly populated into the CamelContext
119     * so that they can be easily browsed by any Camel tooling. This option is disabled by default.
120     *
121     * @param exposeAllQueues
122     */
123    public void setExposeAllQueues(boolean exposeAllQueues) {
124        this.exposeAllQueues = exposeAllQueues;
125    }
126
127    public void setUsePooledConnection(boolean usePooledConnection) {
128        if (getConfiguration() instanceof ActiveMQConfiguration) {
129            ((ActiveMQConfiguration)getConfiguration()).setUsePooledConnection(usePooledConnection);
130        }
131    }
132
133    public void setUseSingleConnection(boolean useSingleConnection) {
134        if (getConfiguration() instanceof ActiveMQConfiguration) {
135            ((ActiveMQConfiguration)getConfiguration()).setUseSingleConnection(useSingleConnection);
136        }
137    }
138
139    protected void addPooledConnectionFactoryService(Service pooledConnectionFactoryService) {
140        pooledConnectionFactoryServiceList.add(pooledConnectionFactoryService);
141    }
142
143    protected void addSingleConnectionFactory(SingleConnectionFactory singleConnectionFactory) {
144        singleConnectionFactoryList.add(singleConnectionFactory);
145    }
146
147    @Override
148    @SuppressWarnings("unchecked")
149    protected String convertPathToActualDestination(String path, Map<String, Object> parameters) {
150        // support ActiveMQ destination options using the destination. prefix
151        // http://activemq.apache.org/destination-options.html
152        Map options = IntrospectionSupport.extractProperties(parameters, "destination.");
153
154        String query;
155        try {
156            query = URISupport.createQueryString(options);
157        } catch (URISyntaxException e) {
158            throw ObjectHelper.wrapRuntimeCamelException(e);
159        }
160
161        // if we have destination options then append them to the destination name
162        if (ObjectHelper.isNotEmpty(query)) {
163            return path + "?" + query;
164        } else {
165            return path;
166        }
167    }
168
169    @Override
170    protected void doStart() throws Exception {
171        super.doStart();
172
173        if (isExposeAllQueues()) {
174            createDestinationSource();
175            endpointLoader = new CamelEndpointLoader(getCamelContext(), source);
176            endpointLoader.afterPropertiesSet();
177        }
178
179        // use OriginalDestinationPropagateStrategy by default if no custom stategy has been set
180        if (getMessageCreatedStrategy() == null) {
181            setMessageCreatedStrategy(new OriginalDestinationPropagateStrategy());
182        }
183    }
184
185    protected void createDestinationSource() {
186        try {
187            if (source == null) {
188                if (connection == null) {
189                    Connection value = getConfiguration().getConnectionFactory().createConnection();
190                    if (value instanceof EnhancedConnection) {
191                        connection = (EnhancedConnection) value;
192                    } else {
193                        throw new IllegalArgumentException("Created JMS Connection is not an EnhancedConnection: " + value);
194                    }
195                    connection.start();
196                }
197                source = connection.getDestinationSource();
198            }
199        } catch (Throwable t) {
200            LOG.info("Can't get destination source, endpoint completer will not work", t);
201        }
202    }
203
204    @Override
205    protected void doStop() throws Exception {
206        if (source != null) {
207            source.stop();
208            source = null;
209        }
210        if (connection != null) {
211            connection.close();
212            connection = null;
213        }
214        for (Service s : pooledConnectionFactoryServiceList) {
215            s.stop();
216        }
217        pooledConnectionFactoryServiceList.clear();
218        for (SingleConnectionFactory s : singleConnectionFactoryList) {
219            s.destroy();
220        }
221        singleConnectionFactoryList.clear();
222        super.doStop();
223    }
224
225    @Override
226    public void setConfiguration(JmsConfiguration configuration) {
227        if (configuration instanceof ActiveMQConfiguration) {
228            ((ActiveMQConfiguration) configuration).setActiveMQComponent(this);
229        }
230        super.setConfiguration(configuration);
231    }
232
233    @Override
234    protected JmsConfiguration createConfiguration() {
235        ActiveMQConfiguration answer = new ActiveMQConfiguration();
236        answer.setActiveMQComponent(this);
237        return answer;
238    }
239
240    @Override
241    public List<String> completeEndpointPath(ComponentConfiguration componentConfiguration, String completionText) {
242        // try to initialize destination source only the first time
243        if (!sourceInitialized) {
244            createDestinationSource();
245            sourceInitialized = true;
246        }
247        ArrayList<String> answer = new ArrayList<String>();
248        if (source != null) {
249            Set candidates = source.getQueues();
250            String destinationName = completionText;
251            if (completionText.startsWith("topic:")) {
252                candidates = source.getTopics();
253                destinationName = completionText.substring(6);
254            } else if (completionText.startsWith("queue:")) {
255                destinationName = completionText.substring(6);
256            }
257
258            Iterator it = candidates.iterator();
259
260            while (it.hasNext()) {
261                ActiveMQDestination destination = (ActiveMQDestination) it.next();
262                if (destination.getPhysicalName().startsWith(destinationName)) {
263                    answer.add(destination.getPhysicalName());
264                }
265            }
266        }
267        return answer;
268    }
269
270    /**
271     * We don't want to ever auto-wire the connection factory from the spring app context.
272     * @return false
273     */
274    public boolean getAllowAutoWiredConnectionFactory() {
275        return false;
276    }
277
278}