jeudi 21 janvier 2010

New in GraniteDS 2.1.0 RC2: more fun with JEE6/CDI

As early testers have discovered, there have a been a few changes in the CDI API that broke the GDS integration since RC1 was released.
GraniteDS 2.1 RC2 now supports the latest CDI specification and Weld RI as included in JBoss 6 M1 and GlassFish v3 final. It can be downloaded here. There have also been improvements in the integration itself :
  • Tide remoting to CDI components (both @Named and type-safe)

  • All standard features of GraniteDS : container security, paging, lazy-loading

  • Support for CDI conversations

  • Support for client-side observers for remote CDI events

  • Client/server synchronization of CDI beans

Il still only works with Weld because of conversation support, but it should be easy to integrate with other implementations by only adapting the CDIInterceptor implementation.
Currently supported containers are JBoss 6.0 M1 and GlassFish v3 final. Some people also managed to get it working in Tomcat 6.

Configuration for JBoss 6 M1

As JBoss 6 does not support servlet 3 yet, the configuration still invoves quite a few files :
  • Add GDS libs in WEB-INF/lib : granite.jar, granite-cdi.jar and granite-hibernate.jar

  • Add AMF (and Gravity if needed) servlets in web.xml

  • Add a WEB-INF/granite/granite-config.xml :
    <granite-config scan="true">
    <security type="org.granite.messaging.service.security.TomcatSecurityService"/>

    <tide-components>
    <tide-component instance-of="org.granite.tide.cdi.Identity"/>
    <tide-component annotated-with="org.granite.messaging.service.annotations.RemoteDestination"/>
    </tide-components>
    </granite-config>

  • Configure the CDI service factory in WEB-INF/flex/services-config.xml :
    <services>
    <service id="granite-service"
    class="flex.messaging.services.RemotingService"
    messageTypes="flex.messaging.messages.RemotingMessage">

    <destination id="cdi">
    <channels>
    <channel ref="graniteamf"/>
    </channels>
    <properties>
    <factory>tideCdiFactory</factory>
    </properties>
    </destination>
    </service>
    </services>
    ...
    <factories>
    <factory id="tideCdiFactory" class="org.granite.tide.cdi.CDIServiceFactory"/>
    </factories>

  • Add an empty beans.xml in WEB-INF

Note that this configuration should also work in Tomcat 6 with a few restrictions.

Configuration for GlassFish v3 and Servlet 3 containers

  • Add GDS libs in WEB-INF/lib : granite.jar, granite-cdi.jar and granite-eclipselink.jar (or the GDS jar corresponding to your JPA provider)

  • Add a configuration class somewhere in your project :
    @FlexFilter(
    tide=true,
    type="cdi",
    factoryClass=CDIServiceFactory.class,
    tideInterfaces={Identity.class}
    )
    public class GraniteConfig {
    }

  • Add an empty beans.xml in WEB-INF

Note that with this kind of simplified configuration, there is no more services-config.xml so it is required to setup the RemoteObject endpoints manually with :
Cdi.getInstance().addComponentWithFactory("serviceInitializer", DefaultServiceInitializer, { contextRoot: "/graniteds-tide-cdi" });

Remoting

Once your application is setup for GraniteDS, you can easily enable remote access from Flex to CDI beans by adding the @RemoteDestination annotation :
@RemoteDestination
public class HelloWorld {

public String hello(String name) {
return "hello" + name;
}
}

If you can live without full type-safety, you can also add a @Named annotation and then access your beans from Flex with :
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
preinitialize="Cdi.getInstance().initApplication()">

<mx:Script>
<![CDATA[
import org.granite.tide.cdi.Cdi;
import org.granite.tide.Component;

[In]
public var helloWorld:Component;
]]>
</mx:Script>

<mx:Button label="Hello" click="helloWorld.hello('Barack')"/>

</mx:Application>

If you don't want string-based references, you can use the gas3 generator to generate a Flex remote proxy for the server bean. Just include the bean class in the gas3 generation path, it will be recognized as a service by the @RemoteDestination annotation. Then you can use type-safe injection in Flex with the new [Inject] Tide annotation :
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
preinitialize="Cdi.getInstance().initApplication()">

<mx:Script>
<![CDATA[
import ...;

[Inject]
public var helloWorld:HelloWorld;
]]>
</mx:Script>

<mx:Button label="Hello" click="helloWorld.hello('Barack')"/>

</mx:Application>

In this case, Tide will do a lookup of the bean by type. If there is more than one implementation, you must add @Named to let Tide choose between them because there is no real equivalent of typesafe annotations in ActionScript 3.

Events

Since RC1, the only change is that gas3 now supports the @TideEvent annotation and is able to generate a corresponding Flex event class.
@RemoteDestination
public class HelloWorld {

@Inject @Any
Event greetingEvent;

public String hello(String name) {
greetingEvent.fire(new GreetingEvent(name));
return "hello" + name;
}
}

<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
preinitialize="Cdi.getInstance().initApplication()">

<mx:Script>
<![CDATA[
import ...;

[Inject]
public var helloWorld:HelloWorld;

[Observer(remote="true")]
public function greet(event:GreetingEvent):void {
trace("Greeting to " + event.name);
}
]]>
</mx:Script>

<mx:Button label="Hello" click="helloWorld.hello('Barack')"/>

</mx:Application>

Client/server bean synchronization

This is quite experimental but it can be useful in some cases. It first requires to enable two interceptors in beans.xml :
<beans>
<interceptors>
<class>org.granite.tide.cdi.TideComponentInterceptor</class>
<class>org.granite.tide.cdi.TideBeanInterceptor</alias>
</interceptors>
</beans>

Note that if you have interceptors to handle transactions, they must be setup before Tide interceptors so these Tide interceptors are executed inside the transaction context.
Then you can annotate beans that you want to synchronize with @TideBean and Tide will ensure that any change made on the client is sent to the server and that any change made on the server bean is updated on the client. This synchronization process is not immediate but always delayed until the next remote call.
@TideBean @RequestScoped
@ExternalizedBean(type=DefaultExternalizer.class)
public class CurrentPerson {

private Person person;
private String greeting;

public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}

public String getGreeting() {
return greeting;
}
public void setGreet(String greeting) {
this.greeting = greeting;
}
}

<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
preinitialize="Cdi.getInstance().initApplication()">

<mx:Script>
<![CDATA[
import ...;

[Inject]
public var helloWorld:HelloWorld;

[Inject]
public var currentPerson:CurrentPerson;

private function hello():void {
currentPerson.person = new Person("Barack");
helloWorld.hello();
}

[Observer(remote="true")]
public function greet(event:GreetingEvent):void {
trace("Greeting to " + currentPerson.greeting);
}
]]>
</mx:Script>

<mx:Button label="Hello" click="hello()"/>

</mx:Application>

@RemoteDestination
public class HelloWorld {

@Inject @Any
Event greetingEvent;

@Inject
CurrentPerson currentPerson;

public void hello() {
String name = currentPerson.getPerson().getName();
currentPerson.setGreeting("hello " + name);
greetingEvent.fire(new GreetingEvent(name));
}
}

Besides the fact that this piece of code is as useless and convoluted as it can be, this bean synchronization feature should work in more useful cases.
Note that the interaction is completely typesafe accross Java/CDI and Flex/Tide, that can help avoiding errors much better than when using string-based EL component names.

2 commentaires:

Anonyme a dit…

Your blog is very enriched. You can see my website Granitemarblestone which is provides Granite benchtops, Caesar Stone Benchtops brisbane, Australia.

Joseph P. a dit…

Understanding these characteristics can help you decide whether granite counters are the right fit for your home and help you to care for them properly if you choose granite.


Kitchen Benchtops