9.3. A Sample Custom Adapter

In this section, I create a simple BlazeDS adapter to illustrate how you could create one, too. The custom adapter extends JavaAdapter and allows you to call multiple POJOs (Plain Old Java Objects, in case you forgot!) class methods, using a single destination. Under normal conditions, one destination maps to a single Java class. Although the custom adapter itself provides a convenient implementation that could be very handy, especially if your application involves remote procedure calls to a large number of server-side Java classes, the purpose here is to illustrate how you could create a custom adapter.

First, look at the invoke method of the multiple POJO adapter, shown in Listing 9-1. The MultiplePOJOAdapter is available as a part of the open source dsadapters project, which aims to provide a number of useful extensions to BlazeDS. Details on the dsadapters project are available at http://code.google.com/p/dsadapters/.

Example 9.1. invoke Method of the MultiplePOJOAdapter
@Override
    public Object invoke(Message message)
    {
        Object result = null;

        try
        {
            RemotingDestination remotingDestination = (RemotingDestination)
               getDestination();
            RemotingMessage remotingMessage = (RemotingMessage)message;
            FactoryInstance factoryInstance = remotingDestination.
               getFactoryInstance();
            String remoteObjectSourceProperty = remotingMessage.getSource();

            boolean sourceMatchesRegEx = false;
            String classToInvoke = null;

if(remoteObjectSourceProperty != null && remoteObjectSourceProperty.
               length() > 0)
            {

                if(sourceRegEx != null && sourceRegEx.length() > 0)
                {
                    try
                    {
                        sourceMatchesRegEx = remoteObjectSourceProperty.matches
                           (sourceRegEx);
                    }
                    catch(PatternSyntaxException patternEx)
                    {
                        throw new MessageException("Invalid regular expression
                           specified " +
                        "in the source attribute");
                    }
                }
                else
                {
                    //since we allow all classes by default
                    sourceMatchesRegEx = true;
                }

                if(sourceMatchesRegEx)
                {
                    classToInvoke = remoteObjectSourceProperty;
                }
                else
                {
                    //user is not allowed to invoke this class
                    throw new IllegalAccessException("This class does not match
                      the specified regular expression");
                    Log.getLogger(LogCategories.MESSAGE_REMOTING).error("source
                       specified in the RemoteObject does not match the
                       configured source regular expression");
                }
            }
            else
            {
                //we are not getting the source value from Flex
                //use the default source
                if(defaultSource != null && defaultSource.length() > 0)
                {
                    classToInvoke = defaultSource;
                }
                else
                {
                    throw new MessageException("No source was specified in the
                       RemoteObject and " +
                        "default source was specified either.");
                }
            }

//check if the class to invoke is in the excluded classes list

            if(excludeClasses.contains(classToInvoke))
            {
                Log.getLogger(LogCategories.MESSAGE_REMOTING).error("The class is
                   in the exclude classes list");
            }

            String methodName = remotingMessage.getOperation();
            List parameters = remotingMessage.getParameters();

            Object instance = createInstance(classToInvoke);

            if (instance == null)
            {
                throw new MessageException("Null instance returned from: " +
                   factoryInstance);
            }
            Class c = instance.getClass();

            MethodMatcher methodMatcher = remotingDestination.getMethodMatcher();
            Method method = methodMatcher.getMethod(c, methodName, parameters);
            result = method.invoke(instance, parameters.toArray());

            saveInstance(instance);
        }
        catch (InvocationTargetException ex)
        {
            Throwable cause = ex.getCause();
            if ((cause != null) && (cause instanceof MessageException))
            {
                logMessage(null, cause);
                throw (MessageException) cause;
            }
            else if (cause != null)
            {
                throw new MessageException(cause.getClass().getName() + " : " +
                   cause.getMessage());
            }
            else
            {
                throw new MessageException(ex.getMessage());
            }
        }
        catdwch (IllegalAccessException ex)
        {
            throw new MessageException(ex.getMessage());
        }

        return result;
    }

The MultiplePOJOAdapter class allows you to specify a regular expression pattern as the value of the source property in your remoting destination. The name of the class whose methods are invoked remotely is specified as the source property of a RemoteObject instance in Flex. A class specified as a value of a RemoteObject's source property needs to satisfy the restrictions imposed by the regular expressions specified as a value to the source property of a remoting destination on the server. Besides, you can explicitly configure classes you don't want to be exposed as remoting destinations.

You know that the invoke method of an adapter is called every time a message is processed by an adapter. So, this is where it's verified that a class name matches the specified regular expression or matches the exclude-classes list. The invoke method of the MultiplePOJOAdapter goes through a series of conditional branches to find out if the destination class name specified in a RemoteObject matches the regular expressions and if it is specified in the exclude-classes list or not. A default class is also specified and calls fall back to this class if the class name specified via the Flex client side is null or undefined.

Each class instance is created, by default, in the request scope. However, you can bind a specific class to a session or the application scope as well. The adapter configuration allows specification of scopes for classes within the standard configuration files, i.e., remoting-config.xml (which you know is included in services-config.xml). A custom property called classes-scopes holds the scope mapping for all those classes whose scopes differ from the default request scope.

Once a class successfully passes through all the guard conditions specified by the destination source regular expression and the exclude-classes list, an instance of the class is created, which is then used to invoke the remote procedure calls. A call to a local method called createInstance from within the invoke method creates an instance of the class. The createInstance method source is:

protected Object createInstance(String classToInvoke) {

        RemotingDestination remotingDestination = (RemotingDestination) getDestination();
        FactoryInstance factoryInstance = remotingDestination.getFactoryInstance();
        JavaFactory factory = (JavaFactory)remotingDestination.getFactory();

        //get scope for this class
        //we are allowing to set scope for classes in configuration file
        String scope = getClassScope(classToInvoke);

        ConfigMap localProperties = new ConfigMap(factoryInstance.getProperties());

        localProperties.put(FlexFactory.SOURCE,classToInvoke);
        localProperties.put(FlexFactory.SCOPE,scope);

        FactoryInstance localFactoryInstance = factory.createFactoryInstance(
                factoryInstance.getId(),
                localProperties);

        Object instance = localFactoryInstance.lookup();

        if (isStarted() && instance instanceof FlexComponent
                && !((FlexComponent)instance).isStarted())
        {
            ((FlexComponent)instance).start();
        }
        return instance;
    }

The createInstance method relies on Java reflection to create an instance of the class, which is identified by its name. An instance is created within the specified scope. Once an instance is created, its start method is called to initialize the component and get it ready.

Before an adapter itself is ready for use, its instantiated and initialized. Two methods called initialize and start run the initial setup tasks. The initialize and start methods for the multiple POJO adapter are:

@Override
    public void initialize(String id, ConfigMap properties)
    {
        super.initialize(id, properties);

        defaultSource = properties.getPropertyAsString(PROPERTY_DEFAULT_SOURCE,null);
        defaultScope = properties.getPropertyAsString(PROPERTY_DEFAULT_SCOPE,null);
        sourceRegEx = properties.getPropertyAsString(PROPERTY_SOURCE, null);

        ConfigMap classesToExclude = properties.getPropertyAsMap
           (PROPERTY_EXCLUDE_CLASSES, null);
           if (classesToExclude != null)
        {
            List classes = classesToExclude.getPropertyAsList(CLASS_ELEMENT, null);

            if ((classes != null) && !classes.isEmpty())
            {
                int n = classes.size();
                for (int i = 0; i < n; i++)
                {
                    ConfigMap classSettings = (ConfigMap)classes.get(i);
                    String name = classSettings.getPropertyAsString(NAME_ATTRIBUTE, null);
                    addExcludeClass(name);
                }
            }
        }

        ConfigMap classesWithDefinedScopes =
          properties.getPropertyAsMap(PROPERTY_CLASSES_SCOPES, null);
        if (classesWithDefinedScopes != null)
        {
            List classes = classesWithDefinedScopes.getPropertyAsList(CLASS_ELEMENT, null);

            if ((classes != null) && !classes.isEmpty())
            {
                int n = classes.size();
                for (int i = 0; i < n; i++)
                {
                    ConfigMap classSettings = (ConfigMap)classes.get(i);
                    String name = classSettings.getPropertyAsString(NAME_ATTRIBUTE, null);
                    String scope = classSettings.getPropertyAsString(SCOPE_ATTRIBUTE, null);
                    addToClassesScopesMap(name,scope);
                }
            }
        }
    }

public void start()
    {
        if (isStarted())
        {
            return;
        }
        super.start();
        validateInstanceSettings();
    }

You will notice that the initialize method is where the configured properties are read, parsed and saved up in local variable. The initialize method, like the invoke method, overrides the corresponding implementations in the JavaAdapter class. The start method on the other hand calls the JavaAdapter class's start method via a call to its super class method, in addition to validating the settings for the instance.

By now you may have realized that writing a custom adapter is fairly straightforward and most of the complexity lies in the business logic your adapter needs to support. An adapter can be created by extending a built-in adapter or one of its abstract super classes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.205.235