Platinum Partner
java

An Account of Developing a Hybrid Java/Flex Application

Mariusz Saternus is a Polish Java Developer, currently working in the UK. He recently became interested in the idea of writing a hybrid Java/Flex application. He wrote an online PDF viewer with a Java back-end. This tutorial follows the steps he took during the development of the application. It is his first enterprise project and therefore it should not be seen as an expert's guide in this field (because it is not!) but an account of developing a hybrid Java/Flex application. You can contact him at mariusz AT idrsolutions DOT com. -- Geertjan Wielenga, Javalobby Zone Leader.

How does it work then?

You can see it for yourself at http://www.jpedal.org/FlexViewer/RC1/Viewer.html.

Use it to browse to a file on disk and then open it:

This is only version One to prove I could get it working. I've got big plans for later versions!

The viewer has two parts. The Java part, which handles all the hardcore PDF parsing and rendering, and the second part, the Flex front end, which is essentially a Flash GUI running in a browser. However, with Adobe AIR coming into play, there is no problem to turn it into a desktop application later.

So, how does it work internally? The user accesses the Flex application and selects the file he/she wants to view. That file is then uploaded to the server where the Java part of the application, in the form of a web service, is running. Next, using Java and a third party PDF library, we decode the page, get the needed information about the file and finally generate an image of the required page of the file we want to view. The link to that image is then sent back to the front end where the Flex app will fetch the image file from the server and display it to the user.

We have to use a third party PDF library simply because neither Flex nor Java provide the required functionality. There are a number of libraries out there to choose from: I'm going to go with JPedal because it is an easy to use API and has excellent rendering abilities. We'll also need a web server to run our Java code because we're going to expose it as a web service, for that reason I'll use Tomcat.

What tools am I going to need?

How to read this tutorial?

The tutorial is divided into two parts:

The parts are then divided into sections to reflect the structure of the project. I'll talk about each of the sections, commenting on important parts of the source code. At the end of each section there will be (if applicable) a link to a file with all the required code in it. The source code files will also contain comments. Whenever I mention things that need to be done in NetBeans, I'll highlight that text in blue, whereas operations performed in Flex Builder 3 will be highlighted in green. Last but not least, I'll be referring to the installation directory of tomcat as CATALINA_HOME throughout this tutorial and in the source code as well.

The Java Side of Things

As mentioned before, to expose the functionality of the components running on the server we'll use a Web Service. In the next step I'll explain how to setup a Web Application project in NetBeans 6.1. Those of you who are familiar with this aspect of Java EE can probably skip the next section.

Creating The Web Service

In this section I shall explain how to set up a Web Application project in NetBeans and add a Web Service to it. To create a new Web Application project click: File->New Project ... a window will appear (screen shot below), at this stage make sure you have selected Web in Categories and Web Application in Projects.



With the correct options selected, click Next. On the next screen, provide a name for the project. In my case, it is JPedal_Server. Apart from that there is no need to change anything else in this section. Click Next. On the new screen make sure you have Tomcat 6.0.16 selected as Server and Java EE 5 as your Java EE Version just like on the screen shot below.



With the correct selections made, click Finish. There is one more screen we could have gone through but it refers to frameworks available to our project, which we won't be making any use of. After the Finish button has been clicked you will notice that the main window in the IDE now displays the content of the index.jsp file. Next step is to add a Web service to our application. To do this you need to right-click the main project node, select New and from the following menu select Web Service.



Once you click the Web Service icon you will be presented with another dialogue window. Here, just provide the Web Service name, in my case PDFImage, and a package, say org.maz. Next, click Finish. A new tab will open in the main window where you will see your Web Service in a design view.



Before we start adding actual code to our service we need to do one more thing. As I mentioned before, we're going to let the Java PDF library deal with all the hardcore PDF parsing and rendering. To be able to do that we need to add the JPedal jar to the CLASSPATH of the project. In NetBeans this is done by right-clicking the main project node and selecting Properties form the menu. After that, we are greeted with another dialogue panel, there we should select Libraries from the Categories list and then click the 'Add JAR/Folder' button. This will allow us to find and select the needed jar.



When done, click OK.

Making Use of the PDF Library in the Service

With the JPedal JAR added to the classpath, we should now have access to a large number of classes which will help us deal with any PDF-related issues. One in which we're especially interested is the PdfDecoder class. This class allows us to (among other things) open a PDF file, find out how many pages has it got or just get an image of a certain page in the document. Let's write our first operation of our web service. In my case I'm going to call it firstStart as it will be the first operation to be executed once the file has been fully uploaded. To create the operation we can either use the wizard provided by the IDE (click the Add Operation button) or we can just type the code in by hand, like this:

// pdf handler object
private PdfDecoder pdf = new PdfDecoder();
// other global variables omitted
@WebMethod(operationName = "firstStart")
public String[] firstStart(String s) {
    if(pdf.isOpen()){
       pdf.closePdfFile();
    }
    try {
      pdf.openPdfFile("LOCATION_OF_UPLOADED_PDF");
    } catch (PdfException ex) {
      // report issue
    }
    // code skipped
    numberOfPages = pdf.getPageCount();
    // generate image of the first page, and provide the path (link) to it.
    String path = this.pdfPath(1);
    return new String[]{path,numberOfPages+""};
}

In the above code the string input parameter is the name of the file the user has selected on the flex side. Having initialised the PDF object at the point of declaration, we first check if we haven't got a file already open (this operation can be called multiple times during a session). If it is, we close the file, dropping all reference to it and preparing the PDF object for the next operation. Next step, we open a PDF file. This is the file which will be uploaded to the server form the
client's side. Where on the server it will be placed depends on the way the upload script is written (more on that further on). We can also ask the PDF object to tell us how many pages the given file has. We'll be using that for navigation on the flex side. What we can also do is call a different operation from the same service, like pdfPath(int). This will generate an image of the first page for me and return a path to it. Finally, we can return the path and the number of pages our document has back to the flex app.

Here, I'll explain how we turn a page of a PDF file into an image:

@WebMethod(operationName = "pdfPath")
public String pdfPath(@WebParam(name = "parameter") int parameter) {
BufferedImage buff = null;
try {
buff = pdf.getPageAsImage(parameter);
} catch (PdfException ex) {
// log error
}
File f = new File(serverPath + "Img/"+ name + "/" + name + "_" + parameter + ".png");
// code omitted
try {
ImageIO.write(buff, "png", f);
} catch (IOException ex) {
// log error
}
return f.getName();
}
The int input parameter is the number of the page we want to generate an image of. Here we make use of the PdfDecoder class again. In short, JPedal returns the required page in a form of a BufferedImage object. We use the java ImageIO to write the buff object into a png file and return its name.

OK, so we allow users to upload files on to the server, and then we generate images (1 per page) and store them on it. The last operation we need is one that will take care of all the mess once we're done, cleanUp(String[]).

@WebMethod(operationName = "cleanUp")
public void cleanUp(@WebParam(name = "fileNameList") String[] fileNameList) {
pdf.closePdfFile();
for(int i=0;i<fileNameList.length;i++){
// code to delete all redundant files
}
}

When this operation is called from Flex, an array containing names of uploaded files will be passed as the input parameter. Due to the convention I used to name generated images (fileName_pageNumber) I can make sure that all the redundant files are deleted once the user closes the application. The important thing we do here is to make sure we close the PDF file (using the PDF object).

Final version of PDFImage.java

Server Side Upload Script


OK, so all this time we were assuming that our PDF file is already on the server. In order for it to get there we need an upload script on the server which will be able to process a request coming in from the client side. In short, on the Flex side we utilize the URLRequest object and pass it the upload script as an URL. The communication between Flex and Java is then handled internally, away from the users eyes. All we have to do is decide how we want to process the incoming information. This is all done inside the upload script. In our case the upload script will take the form of a JSP page. Right click the main project node and choose new and then jsp. Give it a name, in my case fileUpload. Delete the template content of the newly created page. The script code will look something like this:

<%
// code omitted
List items = upload.parseRequest(request);
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();

//handling a normal form-field
if (!item.isFormField()) {
byte[] data = item.get();
File f = new File("/CATALINA_HOME/webapps/ROOT/Flex/Pdf/"+ item.getName());
FileOutputStream fileOutSt = null;
try{
fileOutSt = new FileOutputStream(f);
fileOutSt.write(data);
} catch (Exception e){
e.printStackTrace();
fileOutSt.close();
}
fileOutSt.close();
}
}
%>

The key element here is the “items” object which encapsulates the data (and the operations you can perform on it) sent from the Flex side. What is important is that the script code won't compile the way it is. The reason for that is we are making use of classes that are not included in Java on its own or Tomcat. The jars containing these classes can be found in packages “commons-io-1.4-bin” and “commons-fileupload-1.2.1-bin”. Those jars have to be added to the classpath of the web application project in order for the upload script code to compile (the source and javadoc ones can be skipped).

Final version of fileUpload.jsp

crossdomain.xml: What Is It For?


Flash player, the runtime environment for our Flex application, has many security features. One of them regards communication between different machines over the network and the exchange of data between them. To keep things short and simple: the crossdomain.xml can be seen as a type of list, which is looked up by the Flash player. If the clients' address is on that list then data exchange with the server can take place, otherwise it can't. Due to the nature of our application, we want everyone to be able to upload files to our server and use the web service. The XML file you can find at the bottom of this section has been formatted for this purpose. What is important is where we place the file, it needs to go in the ROOT directory in your CATALINA_HOME (see first image in the "Wrapping Up" section).
<cross-domain-policy>
<allow-http-request-headers-from domain="*" headers="*" secure="false" />
<allow-access-from domain="*" secure="false" />
</cross-domain-policy>
Final version of crossdomain.xml

 

Wrapping Up

OK, assuming we've done everything correctly and we have no errors in our code, the application should be ready to deploy. Before we do that, let's make sure all the files are in the right place and that we have a directory structure on the server which will be able to hold our PDF and PNG files.

Go to CATALINA_HOME (tomcat installation directory). By default it is, on Windows, at “C:\Program Files\Apache Software Foundation\Apache Tomcat 6.0.16”, on OS X (Leopard) “/Library/Tomcat” while on Linux it can be found under “opt/tomcat”. Next, go to the webapps sub directory and then ROOT. In here, create a directory called Flex and in there create one called Pdf and one called Img. This structure must be contained within the ROOT directory as it will have to be accessible through an internet browser. The directory structure should look like this:



I'm assuming you'll be running the web application from NetBeans. Right click the project node and choose run. The application server will start and deploy our project, launching the default internet browser window and a page with the text “Hello World!” should be displayed. Next, we can test if the web service itself works. For this, expand the Web Services node and select our PDFImage service. Right click it and choose “Test Web Service”. If this worked correctly you should see another web page being displayed, this time containing a table with information about the web services exposed by our web app. (image below)










 

Final version of the JPedalServer project



The Flex Side of Things

I'm assuming that most of you know more about Java than Flex. So, again, I'll be a bit pragmatic when it comes to setting up the project and starting off. However, once we're there I'll concentrate on using the web service we created earlier and then leave most of the data presentation,layout and visual stuff for you to learn on your own. There are plenty of tutorials out there and I'd really want to keep this one below 15 pages :P.

Setting Up the Project

In Flex Builder clickFile->New->Flex Project. Give the new project a name, in my case Viewer.



Leave the application type as Web application and click Finish. You will notice a new tab has opened in the main window. That is all that needs doing. At the moment the app does nothing but it compiles and can be deployed instantly to the default browser by clicking the white arrow on a green circle icon. From here, we can change the way our application looks in either a WYSIWYG editor if we are in the design view (click the Design button) or in the Source view (click the Source button). Any changes made in one view will be reflected in the other instantly. Also due to the way Flex Builder 3 is set up, every time we save the project it will be compiled as well. With all this in mind let's add some components which will allow us to make use of our web service.

The Mock Up Viewer

To make explaining how the flex app works easier and the amount of code posted in this tutorial smaller we'll be building a mock up viewer. Sort of a mini version of what you can find here. The source of the full version will be posted at the bottom of this section as well. I think it will be easier to explain certain things this way. After you understand the concepts behind the mock up viewer and then look at the full version you should be able to figure out most of the stuff your self's. All that we'll need to start of is a panel with an image holder on it and a button to invoke our web service. OK, in FB3 (Flex Builder 3) double click your newly created Viewer.mxml (if not already selected). Next click the Source button.



All the presentation code in Flex is written using a XML-like language MXML. Syntax is more or less the same and so are the general concepts. But a Flex app can also make use of actionscript code. This is done by inserting a <mx:Script> section into the mxml code. Just like in the code below:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Panel x="10" y="10" width="486" height="416" layout="absolute">
        <mx:Image id="img" x="10" y="10" width="446" height="326"/>
        <mx:Button x="195.5" y="344" label="Click Me!" click="doStuff()" />
    </mx:Panel>
    <mx:Script>
        <![CDATA[
        import mx.controls.Alert;
        // all action script code goes here
        private function doStuff(): void {
                mx.controls.Alert.show("This is a popup");
        }
        ]]>
    </mx:Script>
</mx:Application>

Looking at the above code we can quickly work out a couple of things. For example in the actionscript code we can comment just like in Java. We can call our AS3 (actionscript 3) code in the mxml code, just like the doStuff() function in the click handler of the button. All the components we add in the mxml context have to be contained within the <mx:Application> tag but outside of <mx:Script> tag. OK, what we have here should be enough to start off.

Using a Web Service in Flex

To be able to make use of a web service in Flex we'll need to utilize the <mx:WebService> component. What we will also need is the URL to our web service. Where do you get the URL from? Assuming you did the Java part, you can get the URL from the table when you test the web service in NetBeans. It should look something like this: 'http://localhost:8084/JPedalServer/PDFImage?wsdl'. OK, now for some code:

<mx:WebService id="ws" wsdl="http://localhost:8084/JPedalServer/PDFImage?wsdl"
fault="handleFirstStartFault(event)"
result="handleFirstStartResult(event)"/>
<mx:Script>
    <![CDATA[
    // some code omitted
    private function handleFirstStartFault(event:FaultEvent):void {
           // executed when something goes wrong
          Alert.show(event.fault.faultString);
    }
    private function handleFirstStartResult(event:ResultEvent):void {
           // executed when the web service returns a result
    }
    private function doStuff(): void {
            // call the firstStart method of the web service
           ws.firstStart("test.pdf");
    }
    ]]>
</mx:Script>

OK, we'll start off with some concepts. The <mx:WebService> component has a result handler and a fault handler. The result handler is executed when the web service finishes and returns a value. The fault result is triggered when something goes wrong, the server is not accessible or there was and error in our Java code on the server. The handleFirstStartResult(event) gets a ResultEvent type object passed in a parameter. That object is a type of a wrapper which encapsulates the result (event.result) of the web service operation as well as other aspects of the web operation, which we are not interested in at the moment. The situation looks similar in case of the handleFristStartFault(event) function. The main difference here is that we have a falult object instead of a result one (event.fault).

To actually call one of the web service operations we have to first make sure we give our <mx:WebService> component an id. In my case it is “ws”. Using the id we can refer to our component in AS3 code, just like I do in the doStuff() function which is assigned as a click listener on our button. The important thing here is that auto completion will not show us any of the web service operations. We just have to type the name of the operation we want to use, and pass it the correct parameters (if any). If we miss spell the operation name or pass in incorrect parameters the fault handler of the web service component will be triggered. For the purpose of simplicity I assumed that there is a file, called test.pdf, already in the correct place on the server, when doStuff() is executed.

So how do you actually get the data from the result object?

private function handleFirstStartResult(event:ResultEvent):void {
    // executed when the web service returns a result
    var a:ArrayCollection = event.result as ArrayCollection;
    // if return type was say String ten
    // var s:String = event.result as String;
    var imageName:String = ObjectUtil.toString(a.getItemAt(0));
    // get rid of the quote marks
    imageName = imageName.substring(1,imageName.length-1);
    // drop the '.png'
    dir = uploadFileName.substring(0,uploadFileName.length-4);
    img.source = serverPath + "Flex/Img/"+ dir+ "/"+ imageName;
    // here we handle the number of pages
    var numberOfPages:String = ObjectUtil.toString(a.getItemAt(1));
    var lenght:int = numberOfPages.length;
    numberOfPages = numberOfPages.substring(1,lenght-1);
    // maxPages is a global var in the Flex app
    this.maxPages = parseInt(numberOfPages);
}
If you remember the web service operation firstStart(String) returns a two element string array. I chose to return values in such way for the purpose of this tutorial. Looking at the above code we can see that in order to cast the result object in to a generic ArrayCollection object we use the key word as. If the type was say String we would have just used event.result as String. We can access elements in the ArrayCollection object using the getItemAt(int) method. If we know that a particular item is a String we can turn it in to one using ObjectUtil.toString(Object). If it is a number we can use parseInt(String) or parseFloat(String). One last thing about the above code. You probably noticed I'm stripping the first and last character of the strings I get from the web service. That is because when you get a String from the Java side it includes the String quote marks.

Uploading Files in Flex


This section ties in with the Java section on uploading files. Uploading files to a server in flex requires two things to be present on a server. The crossdomain.xml and a upload script. Both of there file have their own sections in the Java part of the tutorial. On the flex side we'll be making use of the FileReference object. That object has two very important methods: browse(FileFilter) and upload(URLRequest).

When the browse(FileFilter) method is executed a system file chooser dialog window is open. The user then has a chance to select the desired file. The method can also be called without a FileFilter as a parameter. If that occurs the user can select a file of any type. After the selection is made a reference to the selected file is created, and can later be referred to.

Before using the upload(URLRequest) method, we need to specify a URLRequest object.

var request:URLRequest = new URLRequest("http://localhost:8084/JPedalServer/fileUpload.jsp");
request.method = URLRequestMethod.POST;

It is here that we make use of the upload script we prepared before. We pass the path to the script in the constructor of the URLRequest object and later set the method property to POST, which basically means that the data will be transmitted with the URLRequest object with the HTTP POST method. When everything is ready and ..upload(request) will be executed, the flex application will communicate the the Java Web Service. The crossdomain.xml will be looked up. If communication is allowed the data will be transmitted to the server. There the upload script will intercept the request object and write the data to the file system in whatever way the user define it.

var fr:FileReference = null;

public function browse():void {
   fr = new FileReference();
   var pdfFileter:FileFilter = new FileFilter("Pdf Files","*.pdf");
   fr.browse([pdfFileter]);
}

private function upload():void {
    var request:URLRequest = new URLRequest("http://localhost:8084/JPedalServer/fileUpload.jsp")
    request.method = URLRequestMethod.POST;
    fr.upload(request);
}
 

Final version of the mock up viewer

Final version of the Viewer project

Using a Standalone Version of Tomcat

Some of you may want to use the standalone version of the Tomcat server, instead of the one which comes bundled with NetBeans. There should be no issues with doing so as long as you will remember to edit the source files so they refer to the port 8080 (default Tomcat port) instead of 8084 (if used through NetBeans).

What Next?

At this stage you have all the code you need to run the application. The mock up viewer uses only some of the functionality of the web service. A bit more work has to go into data presentation as well. You can view the source of the Flex Viewer to see I did the image rotation, zooming and all the rest. All the source files (Java and Flex) have comments in them and hopefully explain clearly how things work. Any questions or suggestions are welcome, don't hesitate to email me (mariusz AT idrsolutions DOT com)!

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}