/*
* 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);
}
}