/* * JBoss, Home of Professional Open Source. * Copyright 2013, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.wildfly.extension.messaging.activemq.deployment; import static org.wildfly.extension.messaging.activemq.CommonAttributes.DURABLE; import static org.wildfly.extension.messaging.activemq.CommonAttributes.ENTRIES; import static org.wildfly.extension.messaging.activemq.CommonAttributes.JMS_QUEUE; import static org.wildfly.extension.messaging.activemq.CommonAttributes.JMS_TOPIC; import static org.wildfly.extension.messaging.activemq.CommonAttributes.NAME; import static org.wildfly.extension.messaging.activemq.CommonAttributes.SELECTOR; import static org.wildfly.extension.messaging.activemq.CommonAttributes.SERVER; import static org.wildfly.extension.messaging.activemq.deployment.JMSConnectionFactoryDefinitionInjectionSource.getActiveMQServerName; import static org.wildfly.extension.messaging.activemq.deployment.JMSConnectionFactoryDefinitionInjectionSource.targetsPooledConnectionFactory; import static org.wildfly.extension.messaging.activemq.logging.MessagingLogger.ROOT_LOGGER; import java.util.Map; import javax.jms.Destination; import javax.jms.Queue; import javax.jms.Topic; import org.jboss.as.connector.deployers.ra.AdministeredObjectDefinitionInjectionSource; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.ee.component.InjectionSource; import org.jboss.as.ee.resource.definition.ResourceDefinitionInjectionSource; import org.jboss.as.naming.ContextListAndJndiViewManagedReferenceFactory; import org.jboss.as.naming.ManagedReferenceFactory; import org.jboss.as.server.deployment.Attachments; import org.jboss.as.server.deployment.DeploymentPhaseContext; import org.jboss.as.server.deployment.DeploymentResourceSupport; import org.jboss.as.server.deployment.DeploymentUnit; import org.jboss.as.server.deployment.DeploymentUnitProcessingException; import org.jboss.dmr.ModelNode; import org.jboss.msc.inject.Injector; import org.jboss.msc.service.AbstractServiceListener; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceTarget; import org.wildfly.extension.messaging.activemq.MessagingExtension; import org.wildfly.extension.messaging.activemq.MessagingServices; import org.wildfly.extension.messaging.activemq.jms.JMSQueueConfigurationRuntimeHandler; import org.wildfly.extension.messaging.activemq.jms.JMSQueueService; import org.wildfly.extension.messaging.activemq.jms.JMSTopicConfigurationRuntimeHandler; import org.wildfly.extension.messaging.activemq.jms.JMSTopicService; import org.wildfly.extension.messaging.activemq.jms.WildFlyBindingRegistry; /** * A binding description for JMS Destination definitions. * <p/> * The referenced JMS definition must be directly visible to the * component declaring the annotation. * @author <a href="http://jmesnil.net/">Jeff Mesnil</a> (c) 2013 Red Hat inc. * @author Eduardo Martins */ public class JMSDestinationDefinitionInjectionSource extends ResourceDefinitionInjectionSource { private final String interfaceName; // optional attributes private String description; private String className; private String resourceAdapter; private String destinationName; public JMSDestinationDefinitionInjectionSource(final String jndiName, String interfaceName) { super(jndiName); this.interfaceName = interfaceName; } void setDescription(String description) { this.description = description; } void setClassName(String className) { this.className = className; } void setResourceAdapter(String resourceAdapter) { this.resourceAdapter = resourceAdapter; } void setDestinationName(String destinationName) { this.destinationName = destinationName; } private String uniqueName(InjectionSource.ResolutionContext context) { if (destinationName != null && !destinationName.isEmpty()) { return destinationName; } StringBuilder uniqueName = new StringBuilder(); uniqueName.append(context.getApplicationName() + "_"); uniqueName.append(context.getModuleName() + "_"); if (context.getComponentName() != null) { uniqueName.append(context.getComponentName() + "_"); } uniqueName.append(jndiName); return uniqueName.toString(); } public void getResourceValue(final InjectionSource.ResolutionContext context, final ServiceBuilder<?> serviceBuilder, final DeploymentPhaseContext phaseContext, final Injector<ManagedReferenceFactory> injector) throws DeploymentUnitProcessingException { if (targetsPooledConnectionFactory(getActiveMQServerName(properties), resourceAdapter, phaseContext.getServiceRegistry())) { startActiveMQDestination(context, serviceBuilder, phaseContext, injector); } else { // delegate to the resource-adapter subsystem to create a generic JCA admin object. AdministeredObjectDefinitionInjectionSource aodis = new AdministeredObjectDefinitionInjectionSource(jndiName, className, resourceAdapter); aodis.setInterface(interfaceName); aodis.setDescription(description); // transfer all the generic properties for (Map.Entry<String, String> property : properties.entrySet()) { aodis.addProperty(property.getKey(), property.getValue()); } aodis.getResourceValue(context, serviceBuilder, phaseContext, injector); } } private void startActiveMQDestination(ResolutionContext context, ServiceBuilder<?> serviceBuilder, DeploymentPhaseContext phaseContext, Injector<ManagedReferenceFactory> injector) throws DeploymentUnitProcessingException { final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); final String uniqueName = uniqueName(context); try { ServiceName serviceName = MessagingServices.getActiveMQServiceName(getActiveMQServerName(properties)); if (interfaceName.equals(Queue.class.getName())) { startQueue(uniqueName, phaseContext.getServiceTarget(), serviceName, serviceBuilder, deploymentUnit, injector); } else { startTopic(uniqueName, phaseContext.getServiceTarget(), serviceName, serviceBuilder, deploymentUnit, injector); } } catch (Exception e) { throw new DeploymentUnitProcessingException(e); } } /** * To workaround ActiveMQ's BindingRegistry limitation in {@link WildFlyBindingRegistry} * that does not allow to build a BindingInfo with the ResolutionContext info, the JMS queue is created *without* any * JNDI bindings and handle the JNDI bindings directly by getting the service's JMS queue. */ private void startQueue(final String queueName, final ServiceTarget serviceTarget, final ServiceName serverServiceName, final ServiceBuilder<?> serviceBuilder, final DeploymentUnit deploymentUnit, final Injector<ManagedReferenceFactory> injector) { final String selector = properties.containsKey(SELECTOR.getName()) ? properties.get(SELECTOR.getName()) : null; final boolean durable = properties.containsKey(DURABLE.getName()) ? Boolean.valueOf(properties.get(DURABLE.getName())) : DURABLE.getDefaultValue().asBoolean(); ModelNode destination = new ModelNode(); destination.get(NAME).set(queueName); destination.get(DURABLE.getName()).set(durable); if (selector != null) { destination.get(SELECTOR.getName()).set(selector); } destination.get(ENTRIES).add(jndiName); Service<Queue> queueService = JMSQueueService.installService(queueName, serviceTarget, serverServiceName, selector, durable, new String[0]); inject(serviceBuilder, injector, queueService); //create the management registration String serverName = getActiveMQServerName(properties); final PathElement serverElement = PathElement.pathElement(SERVER, serverName); final PathElement dest = PathElement.pathElement(JMS_QUEUE, queueName); final DeploymentResourceSupport deploymentResourceSupport = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_RESOURCE_SUPPORT); deploymentResourceSupport.getDeploymentSubModel(MessagingExtension.SUBSYSTEM_NAME, serverElement); PathAddress registration = PathAddress.pathAddress(serverElement, dest); MessagingXmlInstallDeploymentUnitProcessor.createDeploymentSubModel(registration, deploymentUnit); JMSQueueConfigurationRuntimeHandler.INSTANCE.registerResource(serverName, queueName, destination); } private void startTopic(String topicName, ServiceTarget serviceTarget, ServiceName serverServiceName, ServiceBuilder<?> serviceBuilder, DeploymentUnit deploymentUnit, Injector<ManagedReferenceFactory> injector) { ModelNode destination = new ModelNode(); destination.get(NAME).set(topicName); destination.get(ENTRIES).add(jndiName); Service<Topic> topicService = JMSTopicService.installService(topicName, serverServiceName, serviceTarget, new String[0]); inject(serviceBuilder, injector, topicService); //create the management registration String serverName = getActiveMQServerName(properties); final PathElement serverElement = PathElement.pathElement(SERVER, serverName); final PathElement dest = PathElement.pathElement(JMS_TOPIC, topicName); final DeploymentResourceSupport deploymentResourceSupport = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_RESOURCE_SUPPORT); deploymentResourceSupport.getDeploymentSubModel(MessagingExtension.SUBSYSTEM_NAME, serverElement); PathAddress registration = PathAddress.pathAddress(serverElement, dest); MessagingXmlInstallDeploymentUnitProcessor.createDeploymentSubModel(registration, deploymentUnit); JMSTopicConfigurationRuntimeHandler.INSTANCE.registerResource(serverName, topicName, destination); } private <D extends Destination> void inject(ServiceBuilder<?> serviceBuilder, Injector<ManagedReferenceFactory> injector, Service<D> destinationService) { final ContextListAndJndiViewManagedReferenceFactory referenceFactoryService = new MessagingJMSDestinationManagedReferenceFactory(destinationService); serviceBuilder.addInjection(injector, referenceFactoryService) .addListener(new AbstractServiceListener<Object>() { public void transition(final ServiceController<? extends Object> controller, final ServiceController.Transition transition) { switch (transition) { case STARTING_to_UP: { ROOT_LOGGER.boundJndiName(jndiName); break; } case START_REQUESTED_to_DOWN: { ROOT_LOGGER.unboundJndiName(jndiName); break; } case REMOVING_to_REMOVED: { ROOT_LOGGER.debugf("Removed messaging object [%s]", jndiName); break; } } } }); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; JMSDestinationDefinitionInjectionSource that = (JMSDestinationDefinitionInjectionSource) o; if (className != null ? !className.equals(that.className) : that.className != null) return false; if (description != null ? !description.equals(that.description) : that.description != null) return false; if (destinationName != null ? !destinationName.equals(that.destinationName) : that.destinationName != null) return false; if (interfaceName != null ? !interfaceName.equals(that.interfaceName) : that.interfaceName != null) return false; if (resourceAdapter != null ? !resourceAdapter.equals(that.resourceAdapter) : that.resourceAdapter != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (interfaceName != null ? interfaceName.hashCode() : 0); result = 31 * result + (description != null ? description.hashCode() : 0); result = 31 * result + (className != null ? className.hashCode() : 0); result = 31 * result + (resourceAdapter != null ? resourceAdapter.hashCode() : 0); result = 31 * result + (destinationName != null ? destinationName.hashCode() : 0); return result; } }