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 * Sets the username to be used to login to ActiveMQ 108 */ 109 public void setUserName(String userName) { 110 if (getConfiguration() instanceof ActiveMQConfiguration) { 111 ((ActiveMQConfiguration)getConfiguration()).setUserName(userName); 112 } 113 } 114 115 /** 116 * Sets the password/passcode used to login to ActiveMQ 117 */ 118 public void setPassword(String password) { 119 if (getConfiguration() instanceof ActiveMQConfiguration) { 120 ((ActiveMQConfiguration)getConfiguration()).setPassword(password); 121 } 122 } 123 124 public void setTrustAllPackages(boolean trustAllPackages) { 125 if (getConfiguration() instanceof ActiveMQConfiguration) { 126 ((ActiveMQConfiguration)getConfiguration()).setTrustAllPackages(trustAllPackages); 127 } 128 } 129 130 public boolean isExposeAllQueues() { 131 return exposeAllQueues; 132 } 133 134 /** 135 * If enabled this will cause all Queues in the ActiveMQ broker to be eagerly populated into the CamelContext 136 * so that they can be easily browsed by any Camel tooling. This option is disabled by default. 137 */ 138 public void setExposeAllQueues(boolean exposeAllQueues) { 139 this.exposeAllQueues = exposeAllQueues; 140 } 141 142 /** 143 * Enables or disables whether a PooledConnectionFactory will be used so that when 144 * messages are sent to ActiveMQ from outside of a message consuming thread, pooling will be used rather 145 * than the default with the Spring {@link JmsTemplate} which will create a new connection, session, producer 146 * for each message then close them all down again. 147 * <p/> 148 * The default value is true. Note that this requires an extra dependency on commons-pool2. 149 */ 150 public void setUsePooledConnection(boolean usePooledConnection) { 151 if (getConfiguration() instanceof ActiveMQConfiguration) { 152 ((ActiveMQConfiguration)getConfiguration()).setUsePooledConnection(usePooledConnection); 153 } 154 } 155 156 /** 157 * Enables or disables whether a Spring {@link SingleConnectionFactory} will be used so that when 158 * messages are sent to ActiveMQ from outside of a message consuming thread, pooling will be used rather 159 * than the default with the Spring {@link JmsTemplate} which will create a new connection, session, producer 160 * for each message then close them all down again. 161 * <p/> 162 * The default value is false and a pooled connection is used by default. 163 */ 164 public void setUseSingleConnection(boolean useSingleConnection) { 165 if (getConfiguration() instanceof ActiveMQConfiguration) { 166 ((ActiveMQConfiguration)getConfiguration()).setUseSingleConnection(useSingleConnection); 167 } 168 } 169 170 protected void addPooledConnectionFactoryService(Service pooledConnectionFactoryService) { 171 pooledConnectionFactoryServiceList.add(pooledConnectionFactoryService); 172 } 173 174 protected void addSingleConnectionFactory(SingleConnectionFactory singleConnectionFactory) { 175 singleConnectionFactoryList.add(singleConnectionFactory); 176 } 177 178 @Override 179 @SuppressWarnings("unchecked") 180 protected String convertPathToActualDestination(String path, Map<String, Object> parameters) { 181 // support ActiveMQ destination options using the destination. prefix 182 // http://activemq.apache.org/destination-options.html 183 Map options = IntrospectionSupport.extractProperties(parameters, "destination."); 184 185 String query; 186 try { 187 query = URISupport.createQueryString(options); 188 } catch (URISyntaxException e) { 189 throw ObjectHelper.wrapRuntimeCamelException(e); 190 } 191 192 // if we have destination options then append them to the destination name 193 if (ObjectHelper.isNotEmpty(query)) { 194 return path + "?" + query; 195 } else { 196 return path; 197 } 198 } 199 200 @Override 201 protected void doStart() throws Exception { 202 super.doStart(); 203 204 if (isExposeAllQueues()) { 205 createDestinationSource(); 206 endpointLoader = new CamelEndpointLoader(getCamelContext(), source); 207 endpointLoader.afterPropertiesSet(); 208 } 209 210 // use OriginalDestinationPropagateStrategy by default if no custom stategy has been set 211 if (getMessageCreatedStrategy() == null) { 212 setMessageCreatedStrategy(new OriginalDestinationPropagateStrategy()); 213 } 214 } 215 216 protected void createDestinationSource() { 217 try { 218 if (source == null) { 219 if (connection == null) { 220 Connection value = getConfiguration().getConnectionFactory().createConnection(); 221 if (value instanceof EnhancedConnection) { 222 connection = (EnhancedConnection) value; 223 } else { 224 throw new IllegalArgumentException("Created JMS Connection is not an EnhancedConnection: " + value); 225 } 226 connection.start(); 227 } 228 source = connection.getDestinationSource(); 229 } 230 } catch (Throwable t) { 231 LOG.info("Can't get destination source, endpoint completer will not work", t); 232 } 233 } 234 235 @Override 236 protected void doStop() throws Exception { 237 if (source != null) { 238 source.stop(); 239 source = null; 240 } 241 if (connection != null) { 242 connection.close(); 243 connection = null; 244 } 245 for (Service s : pooledConnectionFactoryServiceList) { 246 s.stop(); 247 } 248 pooledConnectionFactoryServiceList.clear(); 249 for (SingleConnectionFactory s : singleConnectionFactoryList) { 250 s.destroy(); 251 } 252 singleConnectionFactoryList.clear(); 253 super.doStop(); 254 } 255 256 @Override 257 public void setConfiguration(JmsConfiguration configuration) { 258 if (configuration instanceof ActiveMQConfiguration) { 259 ((ActiveMQConfiguration) configuration).setActiveMQComponent(this); 260 } 261 super.setConfiguration(configuration); 262 } 263 264 @Override 265 protected JmsConfiguration createConfiguration() { 266 ActiveMQConfiguration answer = new ActiveMQConfiguration(); 267 answer.setActiveMQComponent(this); 268 return answer; 269 } 270 271 @Override 272 public List<String> completeEndpointPath(ComponentConfiguration componentConfiguration, String completionText) { 273 // try to initialize destination source only the first time 274 if (!sourceInitialized) { 275 createDestinationSource(); 276 sourceInitialized = true; 277 } 278 ArrayList<String> answer = new ArrayList<String>(); 279 if (source != null) { 280 Set candidates = source.getQueues(); 281 String destinationName = completionText; 282 if (completionText.startsWith("topic:")) { 283 candidates = source.getTopics(); 284 destinationName = completionText.substring(6); 285 } else if (completionText.startsWith("queue:")) { 286 destinationName = completionText.substring(6); 287 } 288 289 Iterator it = candidates.iterator(); 290 291 while (it.hasNext()) { 292 ActiveMQDestination destination = (ActiveMQDestination) it.next(); 293 if (destination.getPhysicalName().startsWith(destinationName)) { 294 answer.add(destination.getPhysicalName()); 295 } 296 } 297 } 298 return answer; 299 } 300 301 /** 302 * We don't want to ever auto-wire the connection factory from the spring app context (requires Camel 2.18 onwards) 303 * 304 * @return false 305 */ 306 public boolean isAllowAutoWiredConnectionFactory() { 307 return false; 308 } 309 310}