Wednesday, February 18, 2009

rspec/cucumber

Time to catch up on some reading...

Behavior driven development (BDD) with rspec and cucumber - don't know much about it yet but PragProg has a new book in beta The RSpec Book and a link to getting that working with JRuby and Java http://wiki.github.com/aslakhellesoy/cucumber/jruby-and-java

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>

Maven2 - BlazeDS, ActiveMQ and Flex project structure

(this is mostly the same as the great article from Sebastien Arbogast, I'm adapting it slightly to our needs and will focus more on the Eclipse/debug integration later on, but you should really read his post)

First up, getting the Maven project laid out!
mvn archetype:create -DgroupId=com.mycompany -DartifactId=myproject
I don't know of any way to generate the parent pom directly, so it's a couple quick steps to clean this up.
2) Go into the "myproject" directory and delete the src folder
3) Edit the pom.xml file and change the packaging element to "pom" instead of "jar" (while you're at it, go ahead and change the JUnit version to 4.5, might as well stay current)

Now that we've got the parent pom set up, let's start adding subprojects. We're going to need a Flex module, you have your choice of a couple projects, but the most promising one seems to be flex-mojos
First a quick stop back at our parent pom.xml file, add in the following before the dependencies element:
<repositories>
<repository>
<id>flex-mojos-repository</id>
<url>http://flex-mojos.googlecode.com/svn/trunk/repository/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>flex-mojos-repository</id>
<url>http://flex-mojos.googlecode.com/svn/trunk/repository/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
Then after the dependencies section add in:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-3</version>
</plugin>
</plugins>
</pluginManagement>
</build>


NOW create the Flex project... (this is all on ONE line)
mvn archetype:create -DarchetypeArtifactId=maven-archetype-flex -DarchetypeVersion=1.0 -DarchetypeGroupId=dk.jacobve.maven.archetypes -DgroupId=mycompany -DartifactId=myproject-ria -DpackageName=com.donbest
If you open up the pom.xml in the parent folder, we should see a new "modules" section
<modules>
<module>mycompany-ria</module>
</modules>

You should also now see another folder in this directory called "myproject-ria". We've got to make a couple quick changes here too... first remove the "properties" element since we're not going to use the "israfil" plugin. Next remove the plugin section itself and replace it with
<plugin>
<groupId>info.rvin.mojo</groupId>
<artifactId>flex-compiler-mojo</artifactId>
<version>1.0-beta7</version>
<extensions>true</extensions>
<configuration>
<locales>
<param>en_US</param>
</locales>
</configuration>
</plugin>

As the last step in the myproject-ria pom.xml, add the Flex dependencies...
<dependencies>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>playerglobal</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>swc</type>
<scope>external</scope>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>flex</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>swc</type>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>framework</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>swc</type>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>framework</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>resource-bundle</type>
<classifier>en_US</classifier>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>rpc</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>swc</type>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>rpc</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>resource-bundle</type>
<classifier>en_US</classifier>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>utilities</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>swc</type>
</dependency>
</dependencies>

Whew, that was rough! But now you should be able to go to the parent folder and run
mvn install

Thursday, February 5, 2009

Jetty and double-slashes in the URL

Hit a really brief snag today with Jetty and Flex...

Basically, in order to make your Flex app portable for different environments, you want to use tokens in the server url
e.g. this snippet from a Flex services-config.xml

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

See the {context.root} part?
If you have this project generate foo.war and deploy it to a server, you would access it through a URL like http://myhost/foo/
So,
server.name=foo
server.port=80
context.root=foo

The Flex client will connect to: http://myhost/foo/messagebroker/amf

Now suppose you want to deploy as the root webapp instead... URL becomes http://myhost/
So,
server.name=foo
server.port=80
context.root=<empty>

The Flex client tries connecting to http://myhost//messagebroker/amf (note the double slash because the context root is actually blank).

This actually works fine in Tomcat and IIS, but Jetty takes a different interpretation of the double slash and throws up a 404.
HTTP ERROR: 404
NOT_FOUND
RequestURI=//messagebroker/amf
Powered by Jetty://

Luckily there's an easy fix if you're using Maven and the jetty-maven-plugin like I am... just edit the 'pom.xml' and add <compactPath>true</compactPath>

<plugins>
...
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>80</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
<webAppConfig>
<contextPath>/</contextPath>
<compactPath>true</compactPath>
</webAppConfig>
</configuration>
</plugin>
...
</plugins>

Sunday, February 1, 2009

long time no blog

Been a few weeks since last post, but I haven't been sitting idle. Got a very simple proof-of-concept going with real-time scores using JMS messaging to a BlazeDS server streaming to an Adobe Flex client (this is all overlaying a legacy system). Pretty exciting stuff and remarkably simple. It's all bleeding edge at this point - going to have to write a something like the guys over at Farata Systems did (NIO connector so we can support enough customers on BlazeDS).