/* * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.flex.core; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletConfig; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.context.ServletConfigAware; import flex.messaging.Destination; import flex.messaging.FlexContext; import flex.messaging.MessageBroker; import flex.messaging.services.Service; import flex.messaging.services.ServiceAdapter; /** * Base class for BlazeDS destination factories. * * @author Jeremy Grelle * @author Mark Fisher */ public abstract class AbstractDestinationFactory implements InitializingBean, DisposableBean, BeanNameAware, BeanFactoryAware, ServletConfigAware { private volatile String destinationId; private volatile String beanName; private String[] channels; private MessageBroker broker; private BeanFactory beanFactory; private String serviceAdapter; private ServletConfig servletConfig; /** * * {@inheritDoc} */ public final void afterPropertiesSet() throws Exception { Assert.notNull(this.broker, "The 'messageBroker' property is required."); try { //These ThreadLocals are needed during destination initialization for JMX MBean registration FlexContext.setThreadLocalMessageBroker(this.broker); FlexContext.setThreadLocalServletConfig(this.servletConfig); Destination destination = this.createDestination(getDestinationId(), this.broker); this.configureAdapter(destination); this.configureChannels(destination); this.initializeDestination(destination); } finally { FlexContext.clearThreadLocalObjects(); } } /** * * {@inheritDoc} */ public final void destroy() throws Exception { if (this.broker == null || !this.broker.isStarted()) { return; } this.destroyDestination(getDestinationId(), this.broker); } /** * * {@inheritDoc} */ public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } /** * * {@inheritDoc} */ public void setBeanName(String name) { this.beanName = name; } /** * Specify the BlazeDS channel ids (in order of preference) for communication with this destination * * @param channels an array of BlazeDS channel ids */ public void setChannels(String[] channels) { this.channels = StringUtils.trimArrayElements(channels); } /** * Specify the id for the destination * * @param destinationId the id to set */ public void setDestinationId(String destinationId) { this.destinationId = destinationId; } /** * Set the {@link MessageBroker} where this destination will be created. * * @param broker the message broker for this destination */ public void setMessageBroker(MessageBroker broker) { this.broker = broker; } /** * Specify a custom service adapter id to be used by this destination * * @param serviceAdapter the custom service adapter id */ public void setServiceAdapter(String serviceAdapter) { this.serviceAdapter = serviceAdapter; } /** * * {@inheritDoc} */ public void setServletConfig(ServletConfig servletConfig) { this.servletConfig = servletConfig; } /** * Configure the service adapter for the destination. * * <p> * This implementation will first search the {@link BeanFactory} for a bean with a matching id and use it if found. * Otherwise the normal <code>createAdapter</code> method on the destination will be called. * * <p> * May be overridden by subclasses that wish to specify custom adapter creation logic. * * @param destination the destination being created */ protected void configureAdapter(Destination destination) { String adapterId = StringUtils.hasText(this.serviceAdapter) ? this.serviceAdapter : getTargetService(this.broker).getDefaultAdapter(); if (this.beanFactory.containsBean(adapterId)) { ServiceAdapter adapter = (ServiceAdapter) this.beanFactory.getBean(adapterId, ServiceAdapter.class); destination.setAdapter(adapter); } else if (destination.getAdapter() == null) { destination.createAdapter(adapterId); } } /** * Create a specific destination and add it to the {@link MessageBroker} * * @param destinationId the id of the destination to create * @param broker the {@link MessageBroker} where the destination should be created * @return the created destination * @throws Exception if the destination could not be created successfully */ protected abstract Destination createDestination(String destinationId, MessageBroker broker) throws Exception; /** * Stops and removes the specified destination from the {@link MessageBroker} * * @param destinationId the id of the destination being destroyed * @param broker the {@link MessageBroker} from which the destination must be removed * @throws Exception if the destination could not be destroyed successfully */ protected abstract void destroyDestination(String destinationId, MessageBroker broker) throws Exception; /** * Expose the BeanFactory to subclasses * * @return the BeanFactory */ protected BeanFactory getBeanFactory() { return this.beanFactory; } /** * Returns the id for the destination created by this factory * * @return the destination id */ protected String getDestinationId() { return StringUtils.hasText(this.destinationId) ? this.destinationId : this.beanName; } /** * Returns the target {@link Service} that will manage the destination * * @param broker the {@link MessageBroker} that controls the service * @return the service */ protected abstract Service getTargetService(MessageBroker broker); /** * Perform any necessary initialization logic on the created {@link Destination} * * @param destination the created destination * @throws Exception if initialization fails */ protected abstract void initializeDestination(Destination destination) throws Exception; private void configureChannels(Destination destination) { if (ObjectUtils.isEmpty(this.channels)) { return; } for (String channelId : this.channels) { Assert.isTrue(this.broker.getChannelIds().contains(channelId), "The channel " + channelId + " is not known to the MessageBroker " + this.broker.getId() + " and cannot be set on the destination " + getDestinationId()); } List<String> channelsList = new ArrayList<String>(); for (String channel : this.channels) { channelsList.add(channel); } destination.setChannels(channelsList); } }