graniteds.orgCommunity Documentation

Chapter 9. Integration with CDI

9.1. Configuration with Servlet 3
9.2. Default Configuration
9.3. Using the Tide API
9.3.1. Basic remoting with dependency injection
9.3.2. Typesafe remoting with dependency injection
9.3.3. Integration with Events
9.4. Messaging with CDI (Gravity)

The Context and Dependency Injection specification is a powerful new feature of Java EE 6. It integrates on a common programming model all the services provided by Java EE.

GraniteDS provides out-of-the-box integration with CDI via the Tide API. You can remotely call CDI beans, and it fully supports serialization of JPA entities from and to your client application, taking care of lazily loaded associations. The support for JPA entity beans is covered in the section JPA and lazy initialization, so this section will only describe how to call CDI components from a Java client. GraniteDS also integrates with container security for authentication and role-based authorization.

The support for CDI is included in the library granite-cdi.jar, so you always have to include this library in either WEB-INF/lib or lib for an ear packaging.

Note

Only the reference implementation Weld is supported for now because of some inconsistencies in a few parts of the spec (notably conversations). This is the one used in JBoss 6 and GlassFish v3.

To provide a more native experience for CDI developers when used in a Servlet 3 compliant container, the CDI support in GraniteDS can be configured with a simple annotated class. The most important features of GraniteDS can be configured this way, and it is still possible to fall back to the default GraniteDS configuration files services-config.xml and granite-config.xml for unsupported features.

On Servlet 3 compliant containers, GraniteDS can use the new APIs to automatically register its own servlets and filters and thus does not need any particular configuration in web.xml. This automatic setup is triggered when GraniteDS finds a class annotated with @ServerFilter in one of the application archives:



@ServerFilter(configProvider=CDIConfigProvider.class)
public class GraniteConfig {
}  
        

The ConfigProvider class defines suitable default values for the CDI integration. It is possible however to override these values by setting them in the annotation properties :



@ServerFilter(
        tide=true,
        type="cdi",
        factoryClass=CDIServiceFactory.class,
        tideInterfaces={Identity.class}
)
public class GraniteConfig {
}  
        

As for any CDI application, don't forget to add a file WEB-INF/beans.xml, even empty. Note than only the Tide API is currently supported out-of-the-box with CDI (there is no basic service factory for RemoteService).

The @ServerFilter declaration will setup an AMF processor for the specified url pattern, and the tide attribute specifies that you want a Tide-enabled service factory. The default url pattern for remoting /graniteamf/amf.txt and messaging /gravityamf/amf.txt.

Other configurations can be done with @ServerFilter:

When using the ConfigProvider allows Tide to search in the CDI context for some of its configuration elements. For now, it will lookup beans that implement ExceptionConverter, AMF3MessageInterceptor or SecurityService and use the existing beans.

If you don't use the Servlet 3 configuration, you will have to use the standard GraniteDS configuration files instead, and setup these elements manually. You can safely skip this section if you choose Servlet 3 configuration.

Here is a default configuration suitable for most cases:



<granite-config scan="true">
    ...
    
    <tide-components>
        <tide-component annotated-with="org.granite.messaging.service.annotations.RemoteDestination"/>
        <tide-component annotated-with="org.granite.tide.annotations.TideEnabled"/>
    </tide-components>
    
</granite-config>    
            


<services-config>

    <services>
        <service id="granite-service"
            class="flex.messaging.services.RemotingService"
            messageTypes="flex.messaging.messages.RemotingMessage">
            <!--
             ! Use "tideCdiFactory" and "my-graniteamf" for "cdi" destination (see below).
             ! The destination must be "cdi" when using Tide with default configuration.
             !-->
            <destination id="cdi">
                <channels>
                    <channel ref="my-graniteamf"/>
                </channels>
                <properties>
                    <factory>tideCdiFactory</factory>
                </properties>
            </destination>
        </service>
    </services>

    <!--
     ! Declare tideCdiFactory service factory.
     !-->
    <factories>
        <factory id="tideCdiFactory" class="org.granite.tide.cdi.CdiServiceFactory"/>
    </factories>

    <!--
     ! Declare my-graniteamf channel.
     !-->
    <channels>
        <channel-definition id="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>    
       

The destination named cdi will be the one and only destination required for all CDI destinations.

Most of what is described in the Tide Remoting section applies for CDI, however GraniteDS also provides a much improved integration with CDI when using the Tide client API.

When using CDI, the only difference on the client is that you must use the cdi destination to build the ServerSession. Here is a simple example of remoting with an injected client proxy for a CDI service:



public class HelloController {
    @Inject @Qualifier("helloService")
    private Component helloService;
    
    public void hello(String to) {
        // Asynchronous call using handlers
        helloService.call("hello", to, new TideResponder<String>() {
            @Override
            public void result(TideResultEvent<String> result) {
                System.out.println("Async result: " + result.getResult());
            }
            
            @Override
            public void fault(TideFaultEvent fault) {
                System.err.println("Fault: " + fault.getFault());
            }
        };
    }
    
    public String helloSync(String to) {    
        // Synchronous wait of Future result
        Future<String> futureResult = helloService.call("hello", to);
        String result = futureResult.get();
        System.out.println("Sync result: " + result);
        return result;
    }
}
            

This is almost identical to the standard Tide API described in the Tide remoting section, and all other methods apply for Spring.

You can benefit from the capability of the Gfx code generator (see here) to generate a strongly typed Java client proxy from the CDI interface when it is annotated with @RemoteDestination. In this case, you can inject a typesafe reference to your service and get better compile time error checking and auto completion in your IDE:



public class HelloController {
    @Inject
    private HelloService helloService;
    
    // Asynchronous call using handlers
    helloService.hello("Barack", new TideResponder<String>() {
        @Override
        public void result(TideResultEvent<String> result) {
            System.out.println("Async result: " + result.getResult());
        }
        
        @Override
        public void fault(TideFaultEvent fault) {
            System.err.println("Fault: " + fault.getFault());
        }
    };
    
    // Synchronous wait of Future result
    Future<String> futureResult = helloService.hello("Barack");
    String result = futureResult.get();
    System.out.println("Sync result: " + result);
}
            

Note that if there are more than one instance of HelloService, you may add the Qualifier annotation to disambiguate the actual server bean name (meaning that the server beans also have to be annotated with @Named).

As with EJB 3 and when using a servlet 3 compliant container, it is possible to configure the three kinds of Gravity topics in the configuration class annotated with @ServerFilter. You can simply add variables to your configuration class annotated with @MessagingDestination, @JmsTopicDestination or @ActiveMQTopicDestination, the name of the variable will be used as destination id.

Simple Topic:



@FlexFilter()
public class MyConfig {
    @MessagingDestination(noLocal=true, sessionSelector=true)
    AbstractMessagingDestination myTopic;
}
       

This declaration supports the properties no-local and session-selector (see the Messaging Configuration section).

You can also define a secure destination by specifying a list of roles required to access the topic:



@MessagingDestination(noLocal=true, sessionSelector=true, roles={ "admin", "user" })
AbstractMessagingDestination myTopic;
       

JMS Topic:



@JMSTopicDestination(noLocal=true, 
    sessionSelector=true, 
    connectionFactory="ConnectionFactory", 
    topicJndiName="topic/myTopic",
    transactedSessions=true,
    acknowledgeMode="AUTO_ACKNOWLEDGE", 
    roles={ "admin", "user" })
AbstractMessagingDestination myTopic;
       

This declaration supports all properties of the default JMS declaration in services-config.xml except for non local initial context environments (see the JMS Integration section).

ActiveMQ Topic:



@ActiveMQTopicDestination(noLocal=true, 
    sessionSelector=true, 
    connectionFactory="ConnectionFactory", 
    topicJndiName="topic/myTopic",
    transactedSessions=true,
    acknowledgeMode="AUTO_ACKNOWLEDGE",
    brokerUrl="vm://localhost",
    createBroker=true,
    waitForStart=true,
    durable=true,
    fileStoreRoot="/opt/activemq/data",
    roles={ "admin", "user" })
AbstractMessagingDestination myTopic;
       

This declaration supports all properties of the default ActiveMQ declaration in services-config.xml except for non-local initial context environments (see the ActiveMQ Integration section).

Finally note that the Gravity singleton that is needed to push messages from the server (see here) is available as a CDI bean and can be injected in any component :



@Inject
private Gravity gravity;