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