Mariusz has posted 1 posts at DZone. View Full User Profile

An Account of Developing a Hybrid Java/Flex Application

09.05.2008
| 35217 views |
  • submit to reddit

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


AttachmentSize
tutorial_html_2e51c903.jpg44.67 KB
tutorial_html_4d0d1668.jpg54.26 KB
tutorial_html_5717e36d.png9.22 KB
tutorial_html_4439460e.png12.88 KB
tutorial_html_m36babc34.gif28.69 KB
tutorial_html_m62eed3d8.png10.66 KB
tutorial_html_m9807edd.jpg41.49 KB
tutorial_html_m38373e52.jpg27.05 KB
tutorial_html_m9768552.png14.54 KB
flex-pdf-viewer.png67.61 KB
PDFImage.java3.62 KB
fileUpload.txt1.63 KB
crossdomain.txt298 bytes
Published at DZone with permission of its author, Mariusz Saternus.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Brian Matzon replied on Mon, 2008/09/08 - 3:11am

It broke my back button ;)

This looks more like a component description thana breakdown on what went well and what didn't.

Why go for flex ? - why not an applet?

Why use java as backend ?

Mariusz Saternus replied on Mon, 2008/09/08 - 4:14am

The reason for not using an applet is because I wanted to avoid a Java dependency on the client in this case - I wanted to use Flex.

If you have an Flex RIA and you want to say display a pdf file you just generated to the user you can't just use an applet. There is no way Flex will be able to 'talk' to the applet. That is what I was interested in - finding ways of communicating between the two technologies. 

The reason for using Java ... well I'm a Java programmer so the choice was obvious. Java is one possible option which may or may not suit everyone.

The article was never meant to be a "breakdown on what went well and what didn't". When I was working on the project I found it hard to find information about the things that I was working on and I wanted to share my findings with others.

Srinivas Veeravalli replied on Tue, 2008/09/09 - 12:27am in response to: Mariusz Saternus

Flex and Java applets can be used in a mixed environment. There are, however, challenges:

  • Making the applet visible. You can do this with a floating iframe, and with the applet as the source of the iframe. You need to set wmode to opaque in Flex for the iframe (and applet) to appear. Else they will go underneath the Flex rectangular area, and will never be visible.
  • Once you achieve the above, there are still z-ordering issues. Essentially the applet will "eat up" all Flex-based visual entities (mouse gestures,Flex dialogs, etc) which overlap with the applet area. There are ways to work around this problem, but these are not very pretty.
  • Finally the communication: you can use JScript (on the Java side) and ExternalInterface (on the Flex side). The glue would of course be JS functions.
So it can be done. Question is whether it is aesthetic enough.

Mariusz Saternus replied on Tue, 2008/09/09 - 12:59am in response to: Srinivas Veeravalli

Thanks for the info. I wasn't aware of such possibilities. There would still be one problem though, what it the users has disabled JS ? Cheers!

Srinivas Veeravalli replied on Tue, 2008/09/09 - 2:59am in response to: Mariusz Saternus

Hmmm.. how would your Flex program run then? AC_OETags.js ring a bell?

 Cheers :-)

Srinivas Veeravalli replied on Tue, 2008/09/09 - 3:02am in response to: Srinivas Veeravalli

Sorry, you are right - it is possible to run Flash without JS.

 But JS presence is a valid assumption in today's Ajax world, I suppose...

 Peace.

Dario Herrera replied on Mon, 2008/09/15 - 6:13am

take a look of Fiji: Exadel Fiji extends JSF by allowing the use of Flex with JSF components and within a JSF page....

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.