Saturday, February 24, 2007

Logging and Debugging deployed Flex applications

I want to share my experience with troubleshooting Flex problem in deployed environment. I am working on a application that uses web service as back end. I do development and basic testing in Flex Builder so I can run debugger and see trace outputs.

But lately I decided to try run my app from web container (tomcat). The first problem I encountered was my login screen doesn't work. I pressed login and nothing happened. I have code that uses Application.application.url parameter to figure out host site and based on url decide where to locate web services. In local debug environment I use hardcoded url (and it works fine), in deployed one I have code that construct url dynamically (and obviously it wasn't working well).

What should I do?

Luckily Flex Builder provides an easy way to run remote application (loaded from web container) in debug mode. First you need to make sure that you used -debug=true flag when you compiled your application. Once you done it, you can create a new debug run configuration in Flex Builder and specify url of deployed application and related project:



Now you just need to set breakpoints, press debug and see why something doesn't work.

Remote debug mode is quite convenient if you have full control over environment. But eventually your application would be deployed in customer site and most likely you would not be able to use debugger to trace down your problem.

To address this problem you need to make sure you have enough log statements in your code that would help to pinpoint the problem so make sure you are aware about mx.logging.* package.

It's very similar to java logging api and should not take long to figure out. The main caveat is that to use default trace facility(mx.logging.targets.TraceTarget) you need to run debug version of flash player. TraceTarget allows you to output message to system.out (eclipse console) or flashlog.txt. To make sure that flashlog.txt is created, you need to check that you have mm.cfg in your user home directory (in my case I didn't have mm.cfg created by installer). If you don't have mm.cfg - create it in C:\Documents and Settings\{username}\( obviously this window specific, check manual for linux path) and populate with parameter you want to use for ex.:

ErrorReportingEnable=1
TraceOutputFileEnable=1
MaxWarnings=100

Now you would be able to see flashlog.txt in c:\Documents and Settings\{username}\Application Data\Macromedia\Flash Player\Logs\.


Most likely you would not be able to use debug flash player in customer environment. You can write your own extension of flex logging classes to make sure you can capture trace event somehow. But the easier way to start with already existing flex logging extension "trace panel" from Adobe Flex Exchange (or directly from author's site http://www.faratasystems.com/?page_id=45) . It provide log capturer win32 application and source code for AbstractTarget that communicates with the win32 app and shows log statement when using none-debug version of flash player.



Hope this was helpful. If know some other techniques that can be used in Flex to simplify problem troubleshooting let me know.

Thursday, February 15, 2007

Handling Web Service exception in Flex code

In this post I'll explain how to propogate exception details from Web Services into Flex. I am using java XFire web service implementation but similar steps can be applied to different ws implementations.

Let assume you defined a web servie method:

public String getSomething() throws ClientException

where ClientException would contain information like exception id and message.

If the method throw an exception, on Flex side you want to extract id and message to present it to user.To do this in Flex we can use standard "fault" handler (I assume you know how to call web service from Flex so I would go into much details there)



service.doSomething.addEventListener("result", responder.result);
service.doSomething.addEventListener("fault", responder.fault);
service.doSomething();


Here we use already defined web service (service) and assign "result" handler to handle normal flow and "fault" handler to handler any exception, including our own ClientException.

responder.fault may look like:



public function fault(info:Object):void {

var ns:Namespace = new Namespace("http://www.acme.com");
default xml namespace = ns;

var xml:XML = new XML(info.fault.faultDetail);

if (xml.id[0] == "123") {
show(xml.message[0]);
}
}



Here we acccess Flex provided info object that includes fault member variable. Using fault var we can access exception details (in this case id and message) and process this data.

So far nothing really special done on Flex side.

Now let's look how to implement server side. I would not go into details how expose web service in XFire (you can find this info in XFire docs). Instead let's focus on Flex specific moments. The most important one is that Flex would not process SOAP fault message if HTTP status code other then 200. Here is excerpt from Flex manual:

> "On web browsers, when a service returns any status code other than 200,
> Adobe Flash Player
> cannot read the body of the response. If the status code is 500 and the
> body contains a
> fault, there is no way to get to the fault. The proxy works around this
> issue by forcing the
> status code for faults to 200; the player passes the body of the
> response along with the
> fault intact. "

It means that we need to add a code on server side to ensure that response code is equal to 200 when we throw an exception. When exception thrown from XFire HTTP status code would be 500 (web services standard implies it), so we need to create a servlet filter that would set HTTP status code to 200 to allow Flex web service engine access SOAP body.

This is also pretty standard stuff, you would need to define filter and associate it with XFire servlet in web.xml (you can find more info on filter here):



Exception filter would like:



import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class ExceptionFilter implements Filter {

public void destroy() {
}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {

HttpServletResponse httpResponse = (HttpServletResponse) response;
ExceptionHttpServletResponseWrapper wrapper = new ExceptionHttpServletResponseWrapper(httpResponse);

chain.doFilter(request, wrapper);
}

public void init(FilterConfig arg0) throws ServletException {
}

}



And you to define HttpServletResponsWrapper that would control setStatus() method to ensure that status code is 200.



import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class ExceptionHttpServletResponseWrapper extends
HttpServletResponseWrapper {

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

@Override
public void setStatus(int statusCode) {
if (statusCode == 500) {
super.setStatus(200);
}
}
}



The above code can be used for any web service implementation.

We still need to add some specific way XFire handles user's exception. One way to it is to create a custom ClientExceptionDetails that contain id and message.



public class ClientExceptionDetails {

private int id;
private String message;

public ClientExceptionDetails(int id, String message) {
super();
this.id = id;
this.message = message;
}

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}



And make sure that ClientException uses this info:



import javax.xml.namespace.QName;

import org.codehaus.xfire.fault.FaultInfoException;
import org.codehaus.xfire.fault.XFireFault;

public class ClientException extends FaultInfoException {

private ClientExceptionDetails faultDetail;

public ClientException(String message, ClientExceptionDetails detail) {
super(message);

this.faultDetail = detail;
}

public ClientExceptionDetails getFaultInfo() {
return faultDetail;
}

public static QName getFaultName() {
return new QName("http://www.acme.com", "ClientException");
}
}



(Check XFire doc for more info about FaultInfoException)

That's all, now when you throw ClientException, XFire would use ClientExceptionDetails to populat SOAP body with id and message that you can extract later on Flex side from fault.faultDetail.