Monday, February 9, 2009

Maven2 - BlazeDS, ActiveMQ and Flex (part2)

Assuming you followed along for "Maven2 - BlazeDS, ActiveMQ and Flex project structure"...

Go into your parent project and create the web module.
mvn archetype:create -DgroupId=mycompany -DartifactId=myproject-web -DarchetypeArtifactId=maven-archetype-webapp
That just created a new module under the parent called "myproject-web". Now edit the pom.xml in that newly created project and add in the dependencies. I'm using Spring and Hibernate at the moment.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.6.ga</version>
</dependency>

Also fix the compiler so that it compiles to 1.6 (1.5 if you prefer).
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>

Ok, it should build at this point. Add your spring context (exercise to the reader) to the src/main/webapp/WEB-INF folder.
Edit the web.xml file so that the webapp knows to load your context.

Next up, a configuration module. Since we're using BlazeDS, there are a couple files that need to be compiled into the SWF as well as present in the webapp. Having them in two places and somehow remembering to keep them in sync is a pretty clear DRY violation. To get around that, we're going to put them in a module all on their own and then add that to the appropriate modules.
mvn archetype:create -DgroupId=mycompany -DartifactId=myapplication-config
We don't need the src/test or src/main/java directories, so delete them and create a src/main/resources directory.
Inside the src/main/resources directory, create the services-config.xml file we'll need. This sets up the channels we'll need for our AMF communication.
<?xml version="1.0" encoding="UTF-8"?>
<services-config>

<services>
<service-include file-path="messaging-config.xml" />
</services>

<security />

<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>false</polling-enabled>
</properties>
</channel-definition>
<channel-definition id="my-streaming-amf" class="mx.messaging.channels.StreamingAMFChannel">
<endpoint url="/messagebroker/streamingamf" class="flex.messaging.endpoints.StreamingAMFEndpoint"/>
<properties>
<idle-timeout-minutes>0</idle-timeout-minutes>
</properties>
</channel-definition>

<channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel">
<endpoint uri="https://{server.name}:9100/{context.root}/messagebroker/amfsecure" class="flex.messaging.endpoints.SecureAMFEndpoint"/>
</channel-definition>

<channel-definition id="my-polling-amf" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://{server.name}:{server.port}/{context.root}/messagebroker/amfpolling" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>true</polling-enabled>
<polling-interval-seconds>8</polling-interval-seconds>
</properties>
</channel-definition>

<channel-definition id="my-rtmp" class="mx.messaging.channels.RTMPChannel">
<endpoint uri="rtmp://{server.name}:2039" class="flex.messaging.endpoints.RTMPEndpoint"/>
<properties>
<idle-timeout-minutes>20</idle-timeout-minutes>
<client-to-server-maxbps>100K</client-to-server-maxbps>
<server-to-client-maxbps>100K</server-to-client-maxbps>
</properties>
</channel-definition>

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

<channel-definition id="my-secure-http" class="mx.messaging.channels.SecureHTTPChannel">
<endpoint uri="https://{server.name}:9100/{context.root}/messagebroker/httpsecure" class="flex.messaging.endpoints.SecureHTTPEndpoint"/>
</channel-definition>
</channels>

<logging>
<target class="flex.messaging.log.ConsoleTarget" level="Debug">
<properties>
<prefix>[Flex] </prefix>
<includeDate>true</includeDate>
<includeTime>true</includeTime>
<includeLevel>true</includeLevel>
<includeCategory>true</includeCategory>
</properties>
<filters>
<pattern>Endpoint.*</pattern>
<pattern>Service.*</pattern>
<pattern>Configuration</pattern>
</filters>
</target>
</logging>

<system>
<redeploy>
<enabled>true</enabled>
<watch-interval>20</watch-interval>
<watch-file>{context.root}/WEB-INF/flex/services-config.xml</watch-file>
<watch-file>{context.root}/WEB-INF/flex/messaging-config.xml</watch-file>
<touch-file>{context.root}/WEB-INF/web.xml</touch-file>
</redeploy>
</system>

</services-config>

Next, add the messaging-config.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<service id="message-service" class="flex.messaging.services.MessageService">
<adapters>
<adapter-definition id="actionscript"
class="flex.messaging.services.messaging.adapters.ActionScriptAdapter"
default="true" />
<adapter-definition id="jms"
class="flex.messaging.services.messaging.adapters.JMSAdapter" />
</adapters>

<destination id="message-destination">
<properties>
<jms>
<destination-type>Topic</destination-type>
<message-type>javax.jms.ObjectMessage
</message-type>
<connection-factory>
java:comp/env/jms/flex/ActiveMqConnectionFactory
</connection-factory>
<destination-jndi-name>java:comp/env/jms/myTopic
</destination-jndi-name>
<delivery-mode>NON_PERSISTENT</delivery-mode>
<message-priority>DEFAULT_PRIORITY
</message-priority>
<acknowledge-mode>AUTO_ACKNOWLEDGE
</acknowledge-mode>
<initial-context-environment>
<property>
<name>Context.INITIAL_CONTEXT_FACTORY
</name>
<value>org.apache.activemq.jndi.ActiveMQInitialContextFactory
</value>
</property>
<property>
<name>Context.PROVIDER_URL</name>
<value>tcp://mycompanyurl:61616
</value>
</property>
</initial-context-environment>
</jms>
</properties>
<channels>
<channel ref="my-streaming-amf" />
</channels>
<adapter ref="jms" />
</destination>
</service>

Next up, we need to edit the myproject-config/pom.xml so that these resources get bundled up into a ZIP file (so we can more easily distribute them). Make a myproject-config/src/main/assembly directory then add a file called resources.xml and copy the following into it:
<assembly>
<id>resources</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory></outputDirectory>
</fileSet>
</fileSets>
</assembly>

And change the myproject-config/pom.xml so that those resources get built. Get rid of the dependencies element since we don't need it. Add a build execution as follows.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make shared resources</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptors>
<descriptor>src/main/assembly/resources.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>


Config module is done (use mvn install to test it, you should get a zip file containing the 2 resources) and now we need to get it into the webapp and Flex modules.
Edit the myproject-web/pom.xml file and add the dependency:
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>myproject-config</artifactId>
<version>1.0-SNAPSHOT</version>
<classifier>resources</classifier>
<type>zip</type>
<scope>provided</scope>
</dependency>

And the code to unpack the resource zip:
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-config</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/flex</outputDirectory>
<includeArtifacIds>todolist-config</includeArtifacIds>
<includeGroupIds>${project.groupId}</includeGroupIds>
<includeClassifiers>resources</includeClassifiers>
<excludeTransitive>true</excludeTransitive>
<excludeTypes>jar,swf</excludeTypes>
</configuration>
</execution>
</executions>
</plugin>


Also into the plugins section you should put the flex-compiler-mojo:
<plugin>
<groupId>info.rvin.mojo</groupId>
<artifactId>flex-compiler-mojo</artifactId>
<extensions>true</extensions>
</plugin>


Since our Flex RIA is also using the config module, edit the myproject-ria/pom.xml and add the dependency just like above:
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>myproject-config</artifactId>
<version>1.0-SNAPSHOT</version>
<classifier>resources</classifier>
<type>zip</type>
<scope>provided</scope>
</dependency>

The plugin configuration is similar to the webapp one, but it has to change just slightly so that the config resources get extracted to a directory to get slurped up by the Flex mojo plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-config</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-resources</phase>
<configuration><outputDirectory>${project.build.directory}/generated-resources</outputDirectory>
<includeArtifacIds>myproject-config</includeArtifacIds>
<includeGroupIds>${project.groupId}</includeGroupIds>
<excludeTransitive>true</excludeTransitive>
</configuration>
</execution>
</executions>
</plugin>

Almost there, now we need to make sure the Flex swf file gets bundled into the web-app, so edit the myproject-web/pom.xml and add it as a dependency (watch out for the <type>swf</type>)
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>myproject-ria</artifactId>
<version>1.0-SNAPSHOT</version>
<type>swf</type>
</dependency>

And the code to unpack it (also in myproject-web/pom.xml):
<execution>
<id>copy-swf</id>
<phase>process-classes</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/${project.build.finalName}</outputDirectory>
<includeTypes>swf</includeTypes>
</configuration>
</execution>

No comments: