Sunday, April 11, 2010

Thoughts on Flash (the Eclipse way)

Funny coincidence but definitely not related to Apple vs. Adobe shootout, my team was required to integrate an existing Adobe Flash application into Zend Studio 7.2.0 (an Eclipse PDT based product). Let's share this experience of integrating Eclipse and Flash:

Reusing Flash or Writing plain SWT?

It started as a simple and common requirement to provide a presentation layer for a given data source loaded into the product. Happy and enthusiastic we started designing a user friendly Eclipse perspective that provides nice diagrams and trees that present the data. Before we started to actually implement it, we were told that it would be great if we can reuse an existing Flash application that our colleges at Zend have already developed. "Adobe do this so why can't we do this?", relating to Flash Builder that is based on Eclipse and Flash. Anyway, it can save months of development and integration effort in the future.

The Good

Using a Flash application into Eclipse integrated web browser is pretty easy task. Users choose a source file in a dedicated import wizard, a new perspective is presented that includes an Eclipse view presenting the Flash application. We used a simple browser view for this end:

public class FlashView extends ViewPart {
private Browser browser;
public void createPartControl(Composite parent) {
parent.setLayout(new FillLayout());
try {
int mozilla = SWT.MOZILLA;
browser = new Browser(parent, mozilla);
refresh();
} catch (SWTError e) {
// ...
}
}
private String getText(String file) {
return "<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'
codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,0,0'
width='100%' height='100%'>
<param name='flashVars' value='...'>
<param name='src' value='myFlash.swf'>
<param name="wmode" value="opaque">
<embed pluginspage='http://www.macromedia.com/go/getflashplayer'
width='100%' height='100%'
flashVars='...'
wmode="opaque"
src='myFlash.swf'/>
</object>"
}
}


And a Jetty server for serving the Flash application is launched:


public class JettyServerManager {

public static void startJettyServer(final int port) throws Exception {
final String rootPath = getJettyRootPath();
Runnable runnable = new Runnable() {
public void run() {
server = new Server();
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(port);

try {
server.start();
server.join();
} catch (Exception e) {
// ...
}
}
};
serverThread = new Thread(runnable);
serverThread.start();
}

public static void stopJettyServer() throws Exception {
if (server != null) {
// ...
}
}
}


The Bad


One more trick that we wanted to add in our client product is a small layer of communication between the two applications, so basically once a user clicks on a data relating to a specific workspace resource the relevant resource is opened in the editor (something like "link with editor" button).

Our first attempt used a dedicated socket to transfer these calls, but after reading the cross-domain policy we understood that another port is required. So three (!!!) different ports are opened for serving this application - the Jetty one, our communication channel and a cross-domain policy port (since the default one is 843 and Linux based OS can't open it we should have open another non-default one). We could live with it if it worked out, but after lots of testing it came out that Windows and Linux machines don't behave the same. On Windows the security port got the request and handles it right, then the actual communication port works as expected. On Linux the same workflow is expected but we got garbage content in our communication although the policy file was excepted and verified by the Flash engine.

Our next attempt used the same Jetty channel to broadcast the changes from the Flash application, once the Flash application wants to alert our product it sends a request to the Jetty (on the same port) and notify our product. This actually worked very nicely.

The Ugly

Well, let's say that Flash and SWT don't share the same look and feel and are naturally different. Usability in this case is a little awkward but tolerable.
To make sure our customers are not required to manually install a browser and then install a Flash player plugin on top of this browser we wanted to have our own XULRunner instance and flash player plugin installed. Although it seems that Adobe's Flash player license is pretty open we couldn't redistribute it in an easy way into our integrated browser as Adobe require to install it under a specific place using their installers.
One last usability shame is the case where an instance of a Flash view is opened and then Eclipse product is closed. Although the Jetty service is down as well (as expected) it should be launched again to serve the view again upon start.

The End Result


A simple import wizard that requires the stored snapshot is displayed:




Many thanks to Qiangsheng Wang and Jacek Pospychala!

6 comments:

Boris Bokowski said...

Why did you not use the JavaScript/Java bridge API of the SWT Browser widget for the communication between your Flash application and the Eclipse side? Using Browser#evaluate, you can call JavaScript from the Java side, and using new BrowserFunction(browser), you can make functionality implemented in Java available as functions on the JavaScript side. This API is available in Eclipse 3.5 and later.

Roy Ganor said...

@boris interesting one, but then we should have also bridge JavaScript and Flash which is another layer. Don't you think it's more complciated than direct http req./res.?

Boris Bokowski said...

@Roy - as always, it depends: different technologies have different tradeoffs. For example, the Java/JavaScript bridge is synchronous (i.e. on the UI thread). This can sometimes make things easier, and sometimes more deadlock-prone ;-). One thing you could try is setting up a simple RPC-style API that allows you to talk between Java and ActionScript without having to touch the JavaScript parts. Or implement a XHR-style API to replace your current approach with minimal effort, perhaps this can help you avoid the platform-specific issues with ports you described.

Roy Ganor said...

@Boris "Or implement a XHR-style API ... this can help you avoid the platform-specific issues with ports you described"
This is exactly what we came up with at the end...

Thanks for your feedback!

Seva (Wsevolod) Lapsha said...

I agree with Boris - JS/flash is a default integration solution. There is no need to bridge them - you can access Flash objects directly from JS. http://www.adobe.com/devnet/flash/javascript_api.html

Zviki Cohen said...

It really is a shame that Adobe didn't step up and provide a Flash container for SWT. They use Eclipse as a base for their products and it is in their interest to promote Flash usage.

I also agree with Boris. I did several dialog boxes using HTML (no Flash) and it worked fine. I also managed to avoid starting an HTTP server instance, which is easier on performance in general.