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