BlazeDS Developer Guide

The factory mechanism

Remoting Service destinations use Java classes that you write to integrate with Flex clients. By default, BlazeDS creates these instances. If they are application-scoped components, they are stored in the ServletContext attribute space using the destination's name as the attribute name. If you use session-scoped components, the components are stored in the FlexSession, also using the destination name as the attribute. If you specify an attribute-id element in the destination, you can control which attribute the component is stored in; this lets more than one destination share the same instance.

The following examples shows a destination definition that contains an attribute-id element:

<destination id="WeatherService">
    <properties>
        <source>weather.WeatherService</source>
        <scope>application</scope>
        <attribute-id>MyWeatherService</attribute-id>
    </properties>
</destination>

In this example, BlazeDS creates an instance of the class weather.WeatherService and stores it in the ServletContext object's set of attributes with the name MyWeatherService. If you define a different destination with the same attribute-id value and the same Java class, BlazeDS uses the same component instance.

BlazeDS provides a factory mechanism that lets you plug in your own component creation and maintenance system to BlazeDS so it integrates with systems like EJB and Spring, which store components in their own namespace. You provide a class that implements the flex.messaging.FlexFactory interface. You use this class to create a FactoryInstance that corresponds to a component configured for a specific destination. Then the component uses the FactoryInstance to look up the specific component to use for a given request. The FlexFactory implementation can access configuration attributes from a BlazeDS configuration file and also can access FlexSession and ServletContext objects. For more information, see the documentation for the FlexFactory class in the public BlazeDS Javadoc documentation.

After you implement a FlexFactory class, you can configure it to be available to destinations by placing a factory element in the factories element in the services-config.xml file, as the following example shows. A single FlexFactory instance is created for each BlazeDS web application. This instance can have its own configuration properties, although in this example, there are no required properties to configure the factory.

<factories>
    <factory id="spring" class="flex.samples.factories.SpringFactory"/>
</factories>

BlazeDS creates one FlexFactory instance for each factory element that you specify. BlazeDS uses this one global FlexFactory implementation for each destination that specifies that factory by its ID; the ID is identified by using a factory element in the properties section of the destination definition. For example, your remoting-config.xml file could have a destination similar to the following one:

<destination id="WeatherService">
    <properties>
        <factory>spring</factory>
        <source>weatherBean</source>
    </properties>
</destination>

When the factory element is encountered at start up, BlazeDS calls the FlexFactory.createFactoryInstance() method. That method gets the source value and any other attributes it expects in the configuration. Any attributes that you access from the ConfigMap parameter are marked as expected and do not cause a configuration error, so you can extend the default BlazeDS configuration in this manner. When BlazeDS requires an instance of the component for this destination, it calls the FactoryInstance.lookup() method to retrieve the individual instance for the destination.

Optionally, factory instances can take additional attributes. There are two places you can do this. When you define the factory initially, you can provide extra attributes as part of the factory definition. When you define an instance of that factory, you can also add your own attributes to the destination definition to be used in creating the factory instance.

The boldface text in the following example shows an attribute added to the factory definition:

<factories>
    <factory id="myFactoryId" class="myPackage.MyFlexFactory">
        <properties>
            <myfactoryattributename>
                myfactoryattributevalue
            </myfactoryattributename>
        </properties>
    </factory>
</factories>

You could use this type of configuration when you are integrating with the Spring Framework Java application framework to provide the Spring factory with a default path for initializing the Spring context that you use to look up all components for that factory. In the class that implements FlexFactory, you would include a call to retrieve the values of the myfactoryattributename from the configMap parameter to the initialize() method in the FlexFactory interface, as the following example shows:

public void initialize(String id, ConfigMap configMap){
    System.out.println("**** MyFactory initialized with: " + 
        configMap.getPropertyAsString("myfactoryattributename", "not set"));
}

The initialize() method in the previous example retrieves a string value where the first parameter is the name of the attribute, and the second parameter is the default value to use if that value is not set. For more information about the various calls to retrieve properties in the config map, see the documentation for the flex.messaging.config.ConfigMap class in the public BlazeDS Javadoc documentation. Each factory instance can add configuration attributes that are used when that factory instance is defined, as the following example shows:

<destination id="myDestination">
    <properties>
        <source>mypackage.MyRemoteClass</source>
        <factory>myFactoryId</factory>
        <myfactoryinstanceattribute>
            myfoobar2value
        </myfactoryinstanceattribute>
    </properties>
</destination>

In the createFactoryInstance() method as part of the FlexFactory implementation, you access the attribute for that instance of the factory, as the following example shows:

public FactoryInstance createFactoryInstance(String id, ConfigMap properties) {
    System.out.println("**** MyFactoryInstance instance initialized with myfactoryinstanceattribute=" + 
    properties.getPropertyAsString("myfactoryinstanceattribute", "not set"));
….
}

The following example shows the source code of a Spring factory class:

package flex.samples.factories;

import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;

import flex.messaging.FactoryInstance;
import flex.messaging.FlexFactory;
import flex.messaging.config.ConfigMap;
import flex.messaging.services.ServiceException;

/**
 * The FactoryFactory interface is implemented by factory components that provide
 * instances to the data services messaging framework.  To configure data services
 * to use this factory, add the following lines to your services-config.xml
 * file (located in the WEB-INF/flex directory of your web application).
 *
<pre>
 *    <factories>
 *     <factory id="spring" class="flex.samples.factories.SpringFactory" />
 *  </factories>
</pre>
 *
 * You also must configure the web application to use spring and must copy the spring.jar
 * file into your WEB-INF/lib directory.  To configure your app server to use Spring,
 * you add the following lines to your WEB-INF/web.xml file:
 *<pre>
 *   <context-param>
 *        <param-name>contextConfigLocation</param-name>
 *        <param-value>/WEB-INF/applicationContext.xml</param-value>
 *   </context-param>
 *
 *   <listener>
 *     <listener-class>
*     org.springframework.web.context.ContextLoaderListener</listener-class>
 *   </listener>
 * </pre>
 * Then you put your Spring bean configuration in WEB-INF/applicationContext.xml (as per the
 * line above).  For example:
 * <pre>
 *  <?xml version="1.0" encoding="UTF-8"?>
 *  <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
 * "http://www.springframework.org/dtd/spring-beans.dtd">
 *   
 *  <beans>
 *    <bean name="weatherBean" class="dev.weather.WeatherService" singleton="true"/>
 *  </beans>
 *  </pre>
 * Now you are ready to define a Remoting Service destination  that maps to this existing service. 
 * To do this you'd add this to your WEB-INF/flex/remoting-config.xml:
 *
 *<pre>
 *  <destination id="WeatherService">
 *      <properties>
 *          <factory>spring</factory>
 *          <source>weatherBean</source>
 *      </properties>
 *  </destination>
 *</pre>
 */
public class SpringFactory implements FlexFactory
{
    private static final String SOURCE = "source";

    /**
     * This method can be used to initialize the factory itself. 
     * It is called with configuration
     * parameters from the factory tag which defines the id of the factory.  
     */
    public void initialize(String id, ConfigMap configMap) {}

    /**
     * This method is called when we initialize the definition of an instance 
     * which will be looked up by this factory.  It should validate that
     * the properties supplied are valid to define an instance.
     * Any valid properties used for this configuration must be accessed to 
     * avoid warnings about unused configuration elements.  If your factory 
     * is only used for application scoped components, this method can simply
     * return a factory instance which delegates the creation of the component
     * to the FactoryInstance's lookup method.
     */
    public FactoryInstance createFactoryInstance(String id, ConfigMap properties)
    {
        SpringFactoryInstance instance = new SpringFactoryInstance(this, id, properties);
        instance.setSource(properties.getPropertyAsString(SOURCE, instance.getId()));
        return instance;
    } // end method createFactoryInstance()

    /**
     * Returns the instance specified by the source
     * and properties arguments.  For the factory, this may mean
     * constructing a new instance, optionally registering it in some other
     * name space such as the session or JNDI, and then returning it
     * or it may mean creating a new instance and returning it.
     * This method is called for each request to operate on the
     * given item by the system so it should be relatively efficient.
     * <p>
     * If your factory does not support the scope property, it
     * report an error if scope is supplied in the properties
     * for this instance.
     * </p>
     */
    public Object lookup(FactoryInstance inst)
    {
        SpringFactoryInstance factoryInstance = (SpringFactoryInstance) inst;
        return factoryInstance.lookup();
    } 

    static class SpringFactoryInstance extends FactoryInstance
    {
        SpringFactoryInstance(SpringFactory factory, String id, ConfigMap properties)
        {
            super(factory, id, properties);
        }

        public String toString()
        {
            return "SpringFactory instance for id=" + getId() + " source=" + getSource() +
                " scope=" + getScope();
        }

        public Object lookup() 
        {
            ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(flex.messaging.FlexContext.getServletConfig().getServletContext());
            String beanName = getSource();

            try
            {
                return appContext.getBean(beanName);
            }
            catch (NoSuchBeanDefinitionException nexc)
            {
                ServiceException e = new ServiceException();
                String msg = "Spring service named '" + beanName + "' does not exist.";
                e.setMessage(msg);
                e.setRootCause(nexc);
                e.setDetails(msg);
                e.setCode("Server.Processing");
                throw e;
            }
            catch (BeansException bexc)
            {
                ServiceException e = new ServiceException();
                String msg = "Unable to create Spring service named '" + beanName + "' ";
                e.setMessage(msg);
                e.setRootCause(bexc);
                e.setDetails(msg);
                e.setCode("Server.Processing");
                throw e;
            } 
        }
        
    } 

} 




 

Send me an e-mail when comments are added to this page | Comment Report

Current page: http://livedocs.adobe.com/blazeds/1/blazeds_devguide/factory_2.html