graniteds.orgCommunity Documentation
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.
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:
tideAnnotations is equivalent to tide-component annotated-with="" in granite-config.xml.
It allows to define the list of annotation names that enable remote access to CDI beans. @RemoteDestination and
@TideEnabled are always declared by default, but you can use any other one if you don't want a compilation dependency
on the GraniteDS libraries.
tideInterfaces is equivalent to tide-component instance-of="" in granite-config.xml.
It allows to define the list of interface/class names that enable remote access to CDI beans.
tideRoles allows to define a list of security roles that are required to access the Tide remote destination.
In general it is not necessary to define this destination-wide security and you can only rely on Java EE security for fine-grained access to individual
beans.
exceptionConverters allows to define a list of server-side exception converters.
It's the equivalent to exception-converters in granite-config.xml.
amf3MessageInterceptor allows to define a message interceptor. You have to define a class implementing
AMFMessageInterceptor. It's highly recommended to subclass org.granite.cdi.CDIInterceptor
and call super.before and super.after in your implementation.
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.
You can define in the tide-annotations section of granite-config.xml the conditions
used to enable remote access to Seam destinations (for example all beans annotated with a particular annotation).
You have to configure the specific Tide/CDI org.granite.tide.cdi.CDIServiceFactory service factory
in services-config.xml.
You have to configure a unique Tide/CDI destination named cdi in services-config.xml
You have to retrieve the Tide context in Flex with Cdi.getInstance().getCdiContext()
instead of Tide.getInstance().getContext().
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).
The Tide client context can register listeners for CDI events triggered on the server-side. The interesting events are sent back along the server response and dispatched at the end of the processing of the result so that the context is correctly synchronized when the event is dispatched.
Here is a simple example:
@Stateful
public class HotelBookingAction implements HotelBooking {
...
@Inject
@Confirmed
private Event<BookingEvent> bookingConfirmedEventSrc;
...
public void confirm() {
em.persist(booking);
bookingConfirmedEventSrc.fire(new BookingEvent(booking));
conversation.end();
}
}
[Observer(remote="true")]
public function bookingConfirmedHandler(event:BookingEvent):void {
Alert.show("Booking confirmed: " + event.booking);
}
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;