Wednesday, March 25, 2009

What's the difference between those who can't read and those who don't?

ANSWER: nothing

Saw that in a /. comment today, but have to admit I got a chuckle out of it.

In the same vein, I think I'm falling in love with LMGTFY.

Tuesday, March 3, 2009

Flex vs. Web Services (pt.2) SOAP faults

Today's fun with Flex came about because one of our developers was trying to figure out how to handle exceptions from a simple user authentication web service.

Basically, there's a few exceptions that can be thrown from the Java code (but this post applies to ANYONE using SOAP faults and flex). Those exceptions end up being wrapped in SOAP faults automatically (as defined in the WSDL).

The Flex Builder web service import wizard successfully builds the Actionscript classes and they seem fine.

Unfortunately, when you send a request that would result in an exception being thrown, you won't see your exception. Instead you'll be able to drill down in the Fault to the following somewhat cryptic message:

Error #2032: Stream Error.

What we discovered is that Flex can't see the body when ANY HTTP status code other than 200 is returned. (There's debate on that - see one of the Flex bugs) The "official" suggested workaround seems to be bastardize your web service so that it returns a 200 status code with a SOAP fault instead of the normal 500 status code. (This also implies you won't be able to use other published web services like Amazon's because they follow the standard with a status code of 500)

Anyhow, assuming you want to hack up your server running the web service and are using Java (we're using CXF, but this is applicable for a servlet container).

1) Create a class extending HttpServletResponseWrapper
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.apache.log4j.Logger;

public class FlexSoapFaultServletResponseWrapper extends HttpServletResponseWrapper {

static final Logger logger = Logger.getLogger(FlexSoapFaultServletResponseWrapper.class);

public FlexSoapFaultServletResponseWrapper(HttpServletResponse response) {
super(response);
}

@Override
public void setStatus(int statusCode) {
if (statusCode == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
super.setStatus(HttpServletResponse.SC_OK);
logger.debug("Converted status code 500 -> 200");
}
}

}
2) Create a servlet filter using that wrapper
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;

public class FlexSoapFaultHandlingHackFilter implements Filter {

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
FlexSoapFaultServletResponseWrapper wrapper = new FlexSoapFaultServletResponseWrapper(httpServletResponse);
chain.doFilter(request, wrapper);
}

@Override
public void init(FilterConfig config) throws ServletException {
}

@Override
public void destroy() {
}
}
3) Define the filter in your WEB-INF/web.xml file and associate it with your CXFServlet
...
<filter>
<filter-name>FlexSoapFaultHandlingHackFilter</filter-name>
<filter-class>com.donbest.services.FlexSoapFaultHandlingHackFilter</filter-class>
</filter>
...
<filter-mapping>
<filter-name>FlexSoapFaultHandlingHackFilter</filter-name>
<servlet-name>CXFServlet</servlet-name>
</filter-mapping>
...
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
...

That's it, now any SOAP fault will be translated to a HTTP status 200 and Flex will be able to handle it. (oh, this might have the much unwanted side-effect of masking any TRUE 500 errors)

Flex vs. Web Services (pt.1) enums

Yes, the title is Flex VERSUS web services (SOAP at least), since we just wasted far too much time trying to figure out some of the quirks.

1) Define a web services including something with an enum (I'm using Java, you use whatever you want)
2) Deploy your webservice. Your WSDL will contain something similar to:
<xs:simpleType name="fooEnum">
<xs:restriction base="xs:string">
<xs:enumeration value="GOOD"/>
<xs:enumeration value="BAD"/>
<xs:enumeration value="UGLY"/>
</xs:restriction>
</xs:simpleType>
3) Generate your actionscript classes from the WSDL. You'll end up with a fooEnum.as file containing
package generated.webservices
{
import mx.utils.ObjectProxy;
import flash.utils.ByteArray;
import mx.rpc.soap.types.*;
/**
* Wrapper class for a operation required type
*/

public class FooEnum
{
/**
* Constructor, initializes the type class
*/
public function FooEnum() {}

[Inspectable(category="Generated values", enumeration="GOOD,BAD,UGLY", type="String")]
public var fooEnum:String;public function toString():String
{
return fooEnum.toString();
}

}
}

4) Try to use the web service. Everything will appear to be fine, EXCEPT that the enum will ALWAYS be null.
If you trace the XML sent down from the server (even using Wireshark), it all looks good.
If you look at the action script classes, they look fine. Eventually you'll find http://bugs.adobe.com/jira/browse/SDK-14800 and some related bugs. Basically, it appears Flex doesn't handle the enums yet (as of SDK 3.x) and the easiest thing to do is change all your enums to Strings =(
Hope you didn't already have people using that web service...

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>