1. Stateless Session Beans

Overview

As indicated by their names, stateless EJB 3 session beans do not save any state between method calls. As such, they are very much like request-scope POJO services, but they bring the ability to manage advanced databases interaction by means of an injected EntityManager and role-based security access control.

Stateless session bean configuration in GraniteDS requires one EJB service factory declaration. Unless you use automatic scanned configuration, declare a destination for each deployed EJB service.

Standard Configuration

Standard configuration uses declarative destination setup in your services-config.xml file.

Let's say we have an EJB 3 stateless session bean defined by those classes:

PersonService.java
package myapp.ejb3.services;

import java.util.List;
import myapp.ejb3.entities.Person;

public interface PersonService {
    public List<Person> findAllPersons();
}

 

PersonServiceBean.java
package myapp.ejb3.services;

import java.util.List;

import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import myapp.ejb3.entities.Person;

@Stateless
@Local(PersonService.class)
public class PersonServiceBean implements PersonService {

    private static final long serialVersionUID = 1L;

    @PersistenceContext
    private EntityManager manager;

    public List<Person> findAllPersons() {
        Query query = manager.createQuery("from " + Person.class.getName());
        return query.getResultList();
    }
}

This code uses an entity bean myapp.ejb3.entities.Person.java that you may implement as you want and that is persisted to a given database. The findAllPersons() method, as indicated by its name, returns a list of all persons currently persisted in the database.

If you package this EJB in a ear under JBoss, say myApp.ear, it will be bound by default to myApp/PersonServiceBean/local.

When you need to access this EJB from your Flex application, you may use a RemoteObject as follows:

Persons.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
    xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
    creationComplete="srv.findAllPersons()">

    <mx:RemoteObject id="srv" destination="person" />

    <!-- Display all person's fields in a DataGrid -->
    <mx:DataGrid id="persons" dataProvider="{srv.findAllPersons.lastResult}" />
</mx:Application>

The glue between the JNDI lookup name myApp/PersonServiceBean/local and the destination name used in your MXML ("person") is given by a services-config.xml file such as the following:

services-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<services-config>

    <services>
        <service
            id="granite-service"
            class="flex.messaging.services.RemotingService"
            messageTypes="flex.messaging.messages.RemotingMessage">
            <destination id="person">
                <channels>
                    <channel ref="my-graniteamf"/>
                </channels>
                <properties>
                    <factory>ejbFactory</factory>
                </properties>
            </destination>
        </service>
    </services>

    <factories>
        <factory id="ejbFactory" class="org.granite.messaging.service.EjbServiceFactory">
            <properties>
                <lookup>myApp/{capitalized.destination.id}ServiceBean/local</lookup>
            </properties>
        </factory>
    </factories>

    <channels>
        <channel-definition id="my-graniteamf" class="mx.messaging.channels.AMFChannel">
            <endpoint
                uri="http://{server.name}:{server.port}/{context.root}/graniteamf/amf"
                class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>

</services-config>

Beside the standard channel configuration, this services-config.xml file declares a service factory named "ejbFactory", which is bound to the org.granite.messaging.service.EjbServiceFactory class, whose lookup property is myApp/{capitalized.destination.id}ServiceBean/local.

In this lookup string, the "{capitalized.destination.id}" part will be resolved at runtime whenever you call a method on the "person" destination, for example findAllPersons(), by the "person" destination id with its first letter capitalized: "Person". Hence, the lookup string becomes myApp/PersonServiceBean/local, that is the actual JNDI binding for our person EJB.

In the services section, one destination is declared with the id "person", bound to the ejbFactory factory. You may declare other destinations as well in the same service block, all bound to the same factory:

services-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<services-config>

    <services>
        <service
            id="granite-service"
            class="flex.messaging.services.RemotingService"
            messageTypes="flex.messaging.messages.RemotingMessage">

            <destination id="person">
                <channels>
                    <channel ref="my-graniteamf"/>
                </channels>
                <properties>
                    <factory>ejbFactory</factory>
                </properties>
            </destination>

            <destination id="product">
                <channels>
                    <channel ref="my-graniteamf"/>
                </channels>
                <properties>
                    <factory>ejbFactory</factory>
                </properties>
            </destination>

            <destination id="billing">
                <channels>
                    <channel ref="my-graniteamf"/>
                </channels>
                <properties>
                    <factory>ejbFactory</factory>
                </properties>
            </destination>

            <!-- etc. -->

        </service>
    </services>

    <factories>
        <factory id="ejbFactory" class="org.granite.messaging.service.EjbServiceFactory">
            <properties>
                <lookup>myApp/{capitalized.destination.id}ServiceBean/local</lookup>
            </properties>
        </factory>
    </factories>

    <channels>
        <channel-definition id="my-graniteamf" class="mx.messaging.channels.AMFChannel">
            <endpoint
                uri="http://{server.name}:{server.port}/{context.root}/graniteamf/amf"
                class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>

</services-config>

Provided that you named your other EJBs "ProductServiceBean" and "BillingServiceBean", they will be looked up using the same principle (myApp/{capitalized.destination.id}ServiceBean/local resolution) and you do not have to declare a separate factory for each of your EJBs.

If you want to enable role-based security checking in your EJB, refer to the GraniteDS Security section "Role-Based Security with EJB 3".

Advanced InitialContext Configuration

You may pass to the InitialContext constructor specific parameters for remote JNDI lookups:

services-config.xml
...
<factory id="ejbFactory" class="org.granite.messaging.service.EjbServiceFactory">
    <properties>
        <lookup>myApp/{capitalized.destination.id}ServiceBean/local</lookup>

        <!-- InitialContext parameters -->
        <initial-context-environment>
            <property>
                <name>Context.PROVIDER_URL</name>
                <value>...</value>
            </property>
            <property>
                <name>Context.INITIAL_CONTEXT_FACTORY</name>
                <value>...</value>
            </property>
            <property>
                <name>Context.URL_PKG_PREFIXES</name>
                <value>...</value>
            </property>
            <property>
                <name>Context.SECURITY_PRINCIPAL</name>
                <value>...</value>
            </property>
            <property>
                <name>Context.SECURITY_CREDENTIALS</name>
                <value>...</value>
            </property>
        </initial-context-environment>
    </properties>
</factory>
...

Please refer to the standard Java Context API documention for details.

Automatic Configuration

It is possible to instruct GraniteDS to automatically search for EJB 3 destinations in the classpath and avoid the above destination configuration in the services-config.xml file.

Note however that this automatic scanning setup cannot work with remote EJBs, as EJB jars will not be available for the scanner, and that you will not be able to use the standard MXML declaration for your RemoteObject(s). Instead of:

<mx:RemoteObject id="srv" destination="person" />

...you should use an ActionScript3 declaration as follows:

(Flex2 declaration)
srv.endpoint = ServerConfig.getChannel("my-graniteamf").endpoint;
srv.destination = "person";
srv.channelSet = new ChannelSet();
srv.channelSet.addChannel(ServerConfig.getChannel("my-graniteamf"));

...or:

(Flex3 simplified declaration)
srv.destination = "person";
srv.channelSet = new ChannelSet();
srv.channelSet.addChannel(ServerConfig.getChannel("my-graniteamf"));

This may be a problem for your existing application, except if you use a framework that encapsulates all RemoteObject(s) creations, such as Tide or Cairngorm.

To enable automatic destinations configuration management, you must first provide a granite-config.xml file that tells GDS to scan jars in the classpath:

granite-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE granite-config PUBLIC
    "-//Granite Data Services//DTD granite-config internal//EN"
    "http://www.graniteds.org/public/dtd/2.0.0/granite-config.dtd">

<granite-config scan="true">
</granite-config>

For each EJB jar you want GDS to scan, you must add a META-INF/services-config.properties file, which may be empty. Then, at application startup time, GDS will scan those jars, looking at classes annotated with the @RemoteDestination annotation. For example, our PersonBean EJB could be now be written as follows:

PersonServiceBean.java
@Stateless
@Local(PersonService.class)
@RemoteDestination(id="person", securityRoles={"user","admin"})
public class PersonServiceBean implements PersonService {
    ...
}

The @RemoteDestination annotation supports the following attributes:

  • id is mandatory and is the destination name
  • service is optional if there is only one service for RemotingMessage defined in services-config.xml. Otherwise this should be the name of the service.
  • channel is optional if there is only one channel defined in services-config.xml. Otherwise this should be the id of the target channel.
  • channels may be used instead of channel to define a failover channel.
  • factory is optional if there is only one factory in services-config.xml. Otherwise this should be the factory id.
  • securityRoles is an array of role names for securing the destination.

As shown below, the service, factory and channel sections are still required in your services-config.xml file, but the service part is shortened by removing all destination configurations. So, with any number of EJBs annotated this way, the services-config.xml file may be shortened as follows:

services-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<services-config>

    <services>
        <service
            id="granite-service"
            class="flex.messaging.services.RemotingService"
            messageTypes="flex.messaging.messages.RemotingMessage">
            <!-- no need to declare destinations here -->
        </service>
    </services>

    <factories>
        <factory id="ejbFactory" class="org.granite.messaging.service.EjbServiceFactory">
            <properties>
                <lookup>myApp/{capitalized.destination.id}ServiceBean/local</lookup>
            </properties>
        </factory>
    </factories>

    <channels>
        <channel-definition id="my-graniteamf" class="mx.messaging.channels.AMFChannel">
            <endpoint
                uri="http://{server.name}:{server.port}/{context.root}/graniteamf/amf"
                class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>

</services-config>

You may have a look at a sample application using those annotations with EJB 3 by importing the graniteds_ejb3 project from the examples folder of the GDS distribution.


Browse Space

- Pages
- Blog
- Labels
- Attachments
- Bookmarks
- Mail
- Advanced

Explore Confluence

- Popular Labels
- Notation Guide

Your Account

Log In

Other Features

Add Content

- Add Comment


SourceForge.net Logo
Copyright © 2007-2010 Adequate Systems. All Rights Reserved.