jeudi 5 novembre 2009

New in GraniteDS 2.1.0 RC1: early support for JSR-299 / JCDI

GraniteDS has almost always had support for EJB3 and JBoss Seam. With the advent of JEE6, support for JSR-299 / JCDI is quite a natural evolution and probably a must have in a not so far future.

So what's in this first implementation :

  • Tide remoting to JCDI Named components

  • All features supported for EJB3: integration with container security, paging, lazy loading

  • Support for JCDI conversations

  • Support for client-side Flex event observers of JCDI typed events


  • Note that for now it will work only with the Weld implementation, as conversation support requires using some non public JSR-299 API.

    Also the supported containers are for now JBoss 5.2 trunk (available here and GlassFish V3 starting from build 70 (available here). It will probably not work in Tomcat for now.

    The GraniteDS distribution contains a graniteds-tide-jcdi example that can be deployed in any of these application servers.

    Configuration

    The configuration is almost the same as for other server framework integrations and consists in five parts :

  • Add the GDS libraries in WEB-INF/lib : granite.jar, granite-jcdi.jar and granite-hibernate.jar (or granite-eclipselink.jar for GlassFish)

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

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

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

  • Configure the Tide JCDI service factory in services-config.xml in WEB-INF/flex :
  • <factories>
    <factory id="tideJcdiFactory" class="org.granite.tide.jcdi.JCDIServiceFactory"/>
    </factories>

  • Add an empty beans.xml in WEB-INF

  • Remoting

    Once this is done, add your named JCDI bean and annotate it with @RemoteDestination :

    @Named("helloWorld")
    @RemoteDestination(id="helloWorld")
    public class HelloWorld {

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

    Then you can easily call it from Flex using Tide remoting and injection :

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

    <mx:Script>
    <![CDATA[
    import org.granite.tide.jcdi.Jcdi;
    import org.granite.tide.Component;

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

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

    </mx:Application>

    You can even use typesafe client proxies (e.g. public var helloWorld:HelloWorld) if you have generated them with gas3. Maybe in RC2 we'll try to use completely typesafe service invocation and will not require @Named beans any more.

    Events

    Support for events is relatively similar to what exists for Seam, but with JCDI it uses typesafe events.
    Define your Java and as3 event classes (in the final release it will be possible to generate the as3 event class automatically with gas3) :
    public class GreetingEvent {
    private String name;

    public GreetingEvent(String name) {
    this.name = name;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    }
    [RemoteClass(alias="test.app.GreetingEvent")]
    public class GreetingEvent extends AbtractTideEvent {
    public var name:String;
    }

    Fire an event from the server method :
    @Named("helloWorld")
    @RemoteDestination(id="helloWorld")
    public class HelloWorld {

    @Inject @Any
    Event greetingEvent;

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

    Then just declare a remote observer in the Flex application and it will be triggered whenever the event is dispatched during a remote call initiated from Flex.
    <mx:Application
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns="*"
    preinitialize="Jcdi.getInstance().initApplication()">

    <mx:Script>
    <![CDATA[
    import org.granite.tide.jcdi.Jcdi;
    import org.granite.tide.Component;

    [In]
    public var helloWorld:Component;

    [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>



    This is really just an early implementation of this integration and it will be updated for the final release of the JCDI specification and RI.

    Our feeling is that JCDI is a perfect fit for Flex RIAs with an event-driven architecture. JCDI applications looks extremely clean and even JBoss Seam provides a lot more features, they do not necessarily make sense with a RIA front-end.

    Feel free to give some feedback and maybe some ideas for this integration.

    New in GraniteDS 2.1.0 RC1: new features of the gas3 generator

    Typesafe proxies


    One of the very nice new features in GraniteDS 2.1 has been contributed by Francesco Faraone and Saverio Trioni.

    If you have already used RemoteObject or the Tide Component/Object API to access remote services, you most likely already have noticed that neither of these options are typesafe.
    What this means is that you can't have code completion in your Flex IDE, and that you can easily make a mistake in either the remote method name or its arguments.

    With GDS 2.1, the gas3 generator is now able to generate typesafe client proxy classes from Java service classes or interfaces. While it can work with service implementations, it's much better to generate the client proxies from service interface when possible. Let's see how this works with the gas3 ant task :

    <target name="generate.as3" depends="define.gas3"
    description="Generate AS3 beans and proxies for example entities and services">

    <gas3 outputdir="flex">
    <classpath>
    <pathelement location="classes"/>
    <path dir="lib"/>
    </classpath>
    <fileset dir="classes">
    <include name="com/myapp/entities/**/*.class"/>
    </fileset>
    <fileset dir="classes">
    <include name="com/myapp/services/**/*Service.class"/>
    </fileset>
    </gas3>
    </target>


    As you can see, this is no different from generating as3 entity beans, just include the interface classes in the generator fileset.

    It's not the only thing to do, because the generator needs to know that is has to generate a client proxy for our interface and not only an as3 interface. So you have to annotate your service interface with @RemoteDestination(id="myService"). If you use GraniteDS for remoting, that should be generally already the case. Here's an example service interface :

    @RemoteDestination(id="personService")
    public interface PersonService {

    public Person createPerson(Person person);

    public Person modifyPerson(Person person);

    public void deletePerson(Integer personId);
    }


    When generating the client for this interface, you will get a PersonService as3 class that extends RemoteObject and can be used exactly as a RemoteObject :

    var personService:PersonService = new PersonService();
    personService.addOperationListener(personService.modifyPerson, ResultEvent.RESULT, handlerFunction);


    This proxy generator can also be used with the Tide remoting API with the option tide="true". You then will be able to use typesafe service proxies either declared manually :

    Tide.getInstance().addComponent("personService", PersonService)

    PersonService(tideContext.personService).modifyPerson(person, modifyResult)

    Or much easier, using implicit declaration and injection :

    [In]
    public var personService:PersonService;

    personService.modifyPerson(person, modifyResult);


    Note that since the generator requires the annotation @RemoteDestination to identity service interface, it's better to use it than @TideEnabled if you want to use typesafe proxies, even if it requires an unused id attribute.

    The service generation can also be done with the Eclipse builder plugin, in this case just add the service interfaces classes in the 'Java Sources' section of the plugin properties page.




    Generation of flex-config.xml



    Another very frequent cause of errors with AMF objects serialization/deserialization comes from the Flex compiler not including all classes in the compiled SWF because they are not referenced anywhere in the code.
    The fix generally consists in adding dummy variables of the missing type, but this is very tedious and it is very easy to forget one.
    It would be a lot easier if the Flex compiler had an option to always include all classes in a source directory (don't hesitate to vote for the following feature request on the Adobe Flex JIRA) but as long as this is not the case, we felt the need to provide something similar ourselves.

    So there is a new option in the gas3 Eclipse builder plugin that automatically builds a flex-config.xml in the root folder of the project including all as3 files from the current source path.



    Then you just have to add the following compiler option :

    -load-config += flex-config.xml

    And you can say goodbye to problems with missing classes.


    Custom entity factory



    If you use the gas3 ant task, you maybe know that it's already possible to define custom templates for all kinds of generated elements and in particular for entity classes with attributes like entitytemplate="blah.gsp" and entitybasetemplate="blahbase.gsp".

    However your custom templates are still limited to use what is provided by the gas3 entity reflection factory. For example it's not easily possible to generate custom as3 annotation from existing Java annotations.
    To allow this, you can now define a custom implementation of EntityFactory that will build a custom instance of JavaEntityBean where you could parse and store whatever information you need from the Java class.

    For example you could build a HVEntityFactory that reads Hibernate Validator 3 annotations to generate custom constraint annotations in the as3 entity class. You could do this with :

    <gas3 entityfactory="com.myapp.MyEntityFactory"
    entitybasetemplate="file:///C:/myapp/myEntityBase.gsp"/>

    mercredi 4 novembre 2009

    New in GraniteDS 2.1.0 RC1: simplified configuration for Seam and Spring

    Hi all,

    Starting a new Flex + GraniteDS + Seam/Spring project or 'granitizing' an existing project is not a very complex task but involves quite a few configuration files and makes hard to get started for new users.

    Following the releases of the Spring-BlazeDS project and the beginning of the Seam-BlazeDS integration, we have implemented a similar mechanism that in most cases requires only one line of specific server configuration for GraniteDS.

    Let's start by Seam :

    The setup of a Seam + GraniteDS project used to require changes or additions of web.xml, components.xml, granite-config.xml and services-config.xml.

    With GDS 2.1, all you need is to add the GDS libraries in WEB-INF/lib or ear/lib and modify components.xml :

    <components xmlns="http://jboss.com/products/seam/components"
    xmlns:core="http://jboss.com/products/seam/core"
    xmlns:security="http://jboss.com/products/seam/security"
    xmlns:transaction="http://jboss.com/products/seam/transaction"
    xmlns:persistence="http://jboss.com/products/seam/persistence"
    xmlns:framework="http://jboss.com/products/seam/framework"
    xmlns:bpm="http://jboss.com/products/seam/bpm"
    xmlns:jms="http://jboss.com/products/seam/jms"
    xmlns:web="http://jboss.com/products/seam/web"
    xmlns:graniteds="http://www.graniteds.org/config"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation=
    "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.0.xsd
    http://jboss.com/products/seam/transaction http://jboss.com/products/seam/transaction-2.0.xsd
    http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.0.xsd
    http://jboss.com/products/seam/web http://jboss.com/products/seam/web-2.0.xsd
    http://jboss.com/products/seam/jms http://jboss.com/products/seam/jms-2.0.xsd
    http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.0.xsd
    http://jboss.com/products/seam/bpm http://jboss.com/products/seam/bpm-2.0.xsd
    http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd
    http://jboss.com/products/seam/framework http://jboss.com/products/seam/framework-2.0.xsd
    http://www.graniteds.org/config http://www.graniteds.org/config/granite-config-2.1.xsd">


    <core:init jndi-pattern="myapp/#{ejbName}/local" debug="true"/>

    <core:manager concurrent-request-timeout="500"
    conversation-timeout="120000" conversation-id-parameter="cid" parent-conversation-id-parameter="pid"/>

    <persistence:entity-manager-factory name="ejb3" persistence-unit-name="ejb3"/>

    <persistence:managed-persistence-context name="entityManager" entity-manager-factory="#{ejb3}"/>

    <security:identity jaas-config-name="other"/>


    <graniteds:flex-filter url-pattern="/graniteamf/*" tide="true"/>

    </components>

    The important part is the flex-filter declaration. It allows to map an url-pattern (in most cases /graniteamf/* is a good default value) to the GraniteDS AMF request processor. The attribute tide="true" defines a Tide/Seam service factory, otherwise this will be a simple Seam service factory.
    This configuration assumes that the Seam filter is mapped in web.xml to /*, which is the case for Seam projects generated by seam-gen or by the Eclipse Seam tools.

    Note that if you need to use Gravity, you still will have to declare manually the Gravity servlet in web.xml and change the filter mapping for SeamFilter. This is because the declaration depends on the servlet container and SeamFilter cannot be used with most container comet implementations (in particular Tomcat 6 CometProcessor).


    Now Spring :

    No big surprise, this is almost exactly the same for Spring, except of course that this time it assumes that a Spring MVC DispatcherServlet is mapped, so there is still some changes in web.xml if you don't have one :

    web.xml :
    <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/graniteamf/*</url-pattern>
    </servlet-mapping>

    applicationContext.xml :
    <beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:graniteds="http://www.graniteds.org/config"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
    http://www.graniteds.org/config http://www.graniteds.org/config/granite-config-2.1.xsd">

    <context:component-scan base-package="com.myapp.service" />

    <tx:annotation-driven transaction-manager="transactionManager"/>

    ...

    <graniteds:flex-filter url-pattern="/*" tide="true"/>

    </beans>

    The configuration is very similar to the one for Seam, except that the url-pattern is this time relative to the url-mapping of the Spring DispatcherServlet. This is also very similar to the configuration for Spring-BlazeDS.

    It is also possible to share an existing DispatcherServlet between a Web/Spring MVC and a GraniteDS/Flex front-ends, only the Flex channel endpoint url will be different (for example http://myserver/myapp/spring/graniteamf/amf instead of http://myserver/myapp/graniteamf/amf).


    In these two new configurations, there is no need for services-config.xml or granite-config.xml, however you can add one for more detailed configuration options.


    The absence of service-config.xml means that the RemoteObject channels can not be defined during the compilation of the Flex application. That means that it will be necessary to provide them manually :

    <mx:Application
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns="*"
    xmlns:cs="test.granite.components.*"
    layout="vertical"
    backgroundGradientColors="[#0e2e7d, #6479ab]"
    preinitialize="preinit()">

    <mx:Script>
    <![CDATA[
    private var graniteChannelSet:ChannelSet;

    private function preinit():void {
    Spring.getInstance().initApplication();

    graniteChannelSet = new ChannelSet();
    graniteChannelSet.addChannel(new AMFChannel("graniteamf", "http://{server.name}:{server.port}/myapp/graniteamf/amf"));

    Spring.getInstance().remoteObjectInitializer = function(ro:RemoteObject):void {
    ro.channelSet = graniteChannelSet;
    };

    }
    ]]>
    </mx:Script>
    ...
    </mx:Application>

    This a bit more complex that simply using services-config.xml, but that also means that you can dynamically configure the channel set, for example using the technique described here.


    This new Seam/Spring configuration can handle a few options that were only in granite-config.xml before :

    <graniteds:flex-filter url-pattern="/*" tide="true">
    <graniteds:tide-annotations>
    <graniteds:value>org.springframework.stereotype.Controller</graniteds:value>
    </graniteds:tide-annotations>
    <graniteds:exception-converters>
    <graniteds:value>com.myapp.util.MyExceptionConverter</graniteds:value>
    </graniteds:exception-converters>
    </graniteds:flex-filter>


    Messaging destinations that were previously declared in services-config.xml can now also be declared in components.xml or applicationContext.xml :

    Simple messaging destination :
    <graniteds:messaging-destination 
    name="addressBookTopicDestination"
    id="addressBookTopic"
    no-local="true"
    session-selector="true"/>


    JMS topic destination :
    <graniteds:jms-topic-destination id="addressBookTopic"
    name="dataTopic"
    connection-factory="ConnectionFactory"
    jndi-name="topic/testTopic"
    acknowledge-mode="AUTO_ACKNOWLEDGE"
    transacted-sessions="true"
    no-local="true"
    session-selector="true"/>



    Hopefully these new configuration options will make easier for new users to get started with GraniteDS and maybe for Spring-BlazeDS users to easily switch to GraniteDS if they want to.