Wednesday, July 27, 2011

ADF 11g : Show PDF in a Popup

In one of my previous posts I showed how to use ADF popup components to display external content such as webpages like wikipedia in an inline frame. Based on this post a colleague of mine tried to display a PDF document. That didn't work. In this post I explain how you can use a servlet to open a PDF document in the inline frame. I will not explain how to invoke popups. If you need to know how to do that, refer to the post mentioned earlier.

How to create the servlet ?

The solution for showing a PDF in a popup is to use a servlet. It's possible to have a servlet deliver PDF content to the browser by specifying the content type of the servlet response to be the 'application/pdf' MIME type via 'response.setContentType("application/pdf")'.
In JDeveloper you can create a HTTP servlet very easy via the new gallery. I decided to call the servlet ShowPdfServlet which actually is a pretty descriptive name for this servlet.


For the servlet mapping I accept the default, meaning that all request containing "/showpdfservlet" in the URL will invoke the servlet. The "create servlet" wizard of JDeveloper will make sure that teh servlet and its mapping is added to the web.xml of your application.


The servlet will get the filename form a request parameter. So in the servlet I can get this value by invoking getParameter on the request.
1: String requestedFile = request.getParameter("name");

With this parameter value, and the file path (which I hardcoded for this post) I am now able to create a new File Object.
1:     // I want to invoke a pdf that is located on the machine where the application is running
2: this.filePath =
"C:\\JDeveloper\\mywork\\11gR2\\showPdfInPopup\\ViewController\\public_html\\WEB-INF\\pdf-docs";
3: // Decode the file name (might contain spaces and on) and prepare file object.
4: File file = new File(filePath, URLDecoder.decode(requestedFile, "UTF-8"));

Next step is to initiate the servlet response:
1: response.reset();
2: response.setBufferSize(DEFAULT_BUFFER_SIZE);
3: response.setContentType(contentType);
4: response.setHeader("Content-Length", String.valueOf(file.length()));
5: response.setHeader("Content-Disposition",
"filename=\"" + file.getName() + "\"");

And now finally get the file and write it to the response.
1:    try {  
2: // Open streams.
3: input =
4: new BufferedInputStream(new FileInputStream(file), DEFAULT_BUFFER_SIZE);
5: output =
6: new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE);
7: // Write file contents to response.
8: byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
9: int length;
10: while ((length = input.read(buffer)) > 0) {
11: output.write(buffer, 0, length);
12: }


How to make sure the servlet is invoked ?

Remember the servlet mapping that I used earlier ? Now I will use this to make sure that I invoke the servlet every time I open the popup containing the iframe. I simply make sure thate the source of the iframe to contains the path that I use in the servletmapping. I also add a 'name' request parameter and set its value to (in this case) a value that I get from a managed bean (line 7).
1:   <af:popup id="popupFileSpecs" animate="default"  
2: contentDelivery="lazyUncached">
3: <af:panelWindow id="pw1" modal="true"
4: title="Extra information for your flight"
5: contentHeight="625" contentWidth="700" resize="on">
6: <af:inlineFrame id="if1" shortDesc="This is an inline frame"
7: source="/showpdfservlet?name=#{pageFlowScope.GeneralBean.pdfUrl}"
8: styleClass="AFStretchWidth" inlineStyle="height:600px;"></af:inlineFrame>
9: </af:panelWindow>
10: </af:popup>


Now run the application, and invoke the popup. There you go !


Simple as that.

Resources

The BalusC Code
What is a servlet ?

A copy of the workspace can be downloaded here.

Tuesday, July 26, 2011

ADF 11g : Printing Directly From Your Application

Last week I was asked this question : "Can we print directly from within our ADF Application, without invoking the printer dialog ?" I wasn't sure but after some investigation the answer was clear. Yes you can ! Here is how...

I decided to create a print start up form where I can select printers and print the document. Most of the functionality needed is provided by the Java Print Service API.

Selecting available printers

I start with a way to show all printers available to the session. For that I simply use the PrinterServiceLookup.
 PrintService[] printers =
PrintServiceLookup.lookupPrintServices(null, null);

The result I can now use to create an Array of SelectItems in order to make the list available in the application.
1:   public SelectItem[] getAllPrinters() {
2: if (allPrinters == null) {
3: PrintService[] printers =
4: PrintServiceLookup.lookupPrintServices(null, null);
5: allPrinters = new SelectItem[printers.length];
6: for (int i = 0; i < printers.length; i++) {
7: SelectItem printer =
8: new SelectItem(printers[i].getName(), printers[i].getName());
9: allPrinters[i] = printer;
10: }
11: }
12: return allPrinters;
13: }

On the pagefragment I use a selectOneChoice component getting the values from the list created above. The code for the pagefragment is like this:
1:    <af:selectOneChoice label="Available Printers" partialTriggers="cb1"  
2: value="#{pageFlowScope.applicationPrinterBean.selectedPrinter}"
3: id="soc1" autoSubmit="true">
4: <f:selectItems value="#{pageFlowScope.applicationPrinterBean.allPrinters}"
5: id="si1"/>
6: </af:selectOneChoice>

When I run the application I see a list of all printers defined om my local machine and I can select any printer I like ...


What is the default printer ?

Usually there is a default printer defined. That is the one I want to use by default in this application. For that to work I need to get hold of the default printer defined. This can be found by invoking lookupDefaultPrintService().
1:   public String getDefaultPrinter() {
2: PrintService defaultPrinter =
3: PrintServiceLookup.lookupDefaultPrintService();
4: return defaultPrinter.getName();
5: }

If no printer is selected yet in the ADF Application, I use the default printer. I need to make an adjustment to the getter of the selected printer to return the default printer.
1:   public String getSelectedPrinter() {
2: if (selectedPrinter == null) {
3: return getDefaultPrinter();
4: } else {
5: return selectedPrinter;
6: }
7: }

Final requirement for the print startup form is to be able to reset the selected printer to the default if another printer was selected previously. For that I use a CommandButton that invokes an actionListener where I simply set the currently selected printer to be the default printer.
1:   public void resetToDefault(ActionEvent actionEvent) {
2: // Add event code here...
3: setSelectedPrinter(getDefaultPrinter());
4: }

By using this button, the default printer will be selected again.


Now what about the actual printing ?

Printing can be done by using the DocPrintJob, FileInputStream and Doc objects.
First is to create a FileInputStream based on the file that I want to print.
Next step is to create a Doc object based on the FileInputStream. Finally create a PrintJob based on the Doc object. I also show the user a message when something goes wrong. All this functionality is implemented in an actionListener behind a command button.
1:   public void invokePrintJob(ActionEvent actionEvent) {
2: // Add event code here...
3: PrintService[] printers =
4: PrintServiceLookup.lookupPrintServices(null, null);
5: Boolean printerFound = false;
6: for (int i = 0; i < printers.length && !printerFound; i++) {
7: if (printers[i].getName().equalsIgnoreCase(getSelectedPrinter())) {
8: printerFound=true;
9: PrintService printer = printers[i];
10: FileInputStream input;
11: try {
12: input =
13: new FileInputStream(theFile);
14: Doc doc = new SimpleDoc(input, DocFlavor.INPUT_STREAM.PNG, null);
15: PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
16: DocPrintJob job = printer.createPrintJob();
17: job.print(doc, attrs);
18: } catch (PrintException e) {
19: System.out.println(e.getMessage());
20: addFacesErrorMessage(e.getMessage());
21: } catch (FileNotFoundException e) {
22: System.out.println(e.getMessage());
23: addFacesErrorMessage(e.getMessage());
24: }
25: }
26: }
27: }

The code for the page fragment is simple.
1:    <af:toolbar>    
2: <af:commandToolbarButton text="Reset to Default"
3: actionListener="#{pageFlowScope.applicationPrinterBean.resetToDefault}"
4: partialSubmit="true" id="cb1"/>
5: <af:commandToolbarButton text="Print" id="ctb1"
6: actionListener="#{pageFlowScope.applicationPrinterBean.invokePrintJob}"/>
7: </af:toolbar>

And the page now has a print button.

When the print button is pushed, the document is sent directly to the printer.

It is also possible to set job properties such as number of copies, the orientation of the print, and many other properties.
1:  attrs.add(new Copies(2));
2: attrs.add(OrientationRequested.LANDSCAPE);

I can check the result of this in the print properties of the printjob.

Now one last thing to do is to give the printjob a decent name. As you can see, by default, all jobs have the same name, and that has no relation whatsoever with the document name, neither can it be related to a user or client.

I want to use a jobname that is more descriptive. I can do that by adding the JobName to the PrintRequestAttributeSet.
1:  PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
2: attrs.add(new Copies(2));
3: attrs.add(OrientationRequested.LANDSCAPE);
4: JobName name = new JobName(file ,null);
5: attrs.add(name);

The result is obvious.


Conclusion
http://www.blogger.com/img/blank.gif
Printing can be done directly from within the ADF Application. You can use almost any of the properties that can be used in the printer dialog of your browser or operating system. This example can be extended and I will probably create an ADF taskflow library to deploy this functionality.

Resources : javax.print javadoc

You can request a copy of the workspace by emailing me at lucbors at gmail dot com. OR you can download the workspace here.

Monday, July 25, 2011

ODTUG Kscope 11: My story on ADF in the Fusion MiddleWare Track

From June 26th to June 30th the KScope 11 conference was in Long Beach California. Nice setting for a great conference. It was the first time that KScope had a Fusion Middleware Track. For me it was a very busy conference with three presentations, handson workshops, live demonstrations and a Boff session with the EMG. In this post some of my highlights.



Friday and Saturday
On Friday in the airplane and Saturday in Long Beach I wrote a review for Sten Vesterli's book. The review can be found here on the AMIS Technology Blog.

Sunday Symposium
The symposium started very early. That is 7.30 including breakfast and registration.
Then some great sessions on ADF and SOA and Webcenter. Chris Muir and John King talked about ADF, Sten Vesterli and Hajo Norman about WebCenter, Lonneke Dikmans and Chris Judson talked about SOA and BPM. Finally Madhuri Kolhatkar \was presenting on User Experience Design Patterns. She pointed out a very usefull resource where you can find OBIEE Design Patterns that are just as well applicable to FMW and ADF Applications. Image
Too bad that currently the site is down (http://blogs.oracle.com/usableapps/entry/usableapps_web_site_down).

Monday
Day started after a hilarious opening session in the Long Beach Performing Arts Center. I attended two sessions and spent some time on preparing my own sessions for Tuesday. The first session "WebCenter Spaces: A Beginner's Guide to Enterprise 2.0 By Maicho Rocha of Oracle Corporation."
In this webcenter spaces session maiko showed how to recreate the kscope website with spaces. Very nice. Maiko also revealed that Webcenter Release 12 will introduce 'iGoogle' like functionality to add components to your personal space. Maiko had a live kscope WebCenter portal that we could play with during his presentation. Very cool indeed !

The other one "Introduction to JPA and How to Use it with Oracle ADF by Shay Shmeltzer of Oracle Corporation." Introducing the key concepts of JPA and explain how it works. It will show you how you can leverage JPA as the database access layer in an Oracle ADF application, and what special features you get from this integration. I didn't learn a lot new features, however, the fact that in JDeveloper 11gR2 named criteria, UI hints, validations and more ADF BC related features are also available for JPA was new for me. As was the fact that named criteria defined on a JPA entity can be dragged from the datacontrol and dropped as an adf queryform.

Monday evening there was the ADF-EMG Boff Session. A (for a Boff) nice headcount in the audience (about 25), and we discussed existing and new topics for the EMG, derived from the 'new member questionnaire'.

Tuesday
A busy (Actually way too busy) day for me with two sessions. The presentations can be found on slideshare. The first one "ADF Developers : Make the database work for you" did go very good. In this session I discussed the various points of interaction between database and ADF and when to use which. I also highlighted some special database features - flashback, View with Instead Of Trigger, and Analytical Functions and DB Query ResultSet Change Notification.

The second one : "A Picture paints a thousand words" did also go pretty good. It was about how to present data with ADF DVT and how to use DVT components for data analysis. From pretty straightforward usage of gauges, graphs, and maps, to more sophisticated features like time selectors, drill down functionality, data manipulation in DVT, and real time update via server push. A had a good crowd, considering that @DuncanMills was presenting next door on ADF debugging.

In between I also managed to follow two other sessions. The first one called Going Social: Enriching Your ADF Applications with Webcenter by Maiko Rocha of Oracle Corporation.

The second one Skinning ADF - Getting the Look You Want by Shay Shmeltzer of Oracle Corporation.
Some very nice demos of the new visual skin editor. Also a nice tip on how to use firebug in order to know what elements you actually need to skin. Just add an entry to the web.xml file to prevent ADF Faces from compressing the HTML that is generated. The entry is for you will be looking for is:
org.apache.myfaces.trinidad.DISABLE_CONTENT_COMPRESSION
Set it to true. This is very useful when you work on creating the skin, but don't forget to un-set it before you go production. And look what was created in just under an hour.....


More resources on this session can be found here : the video of the #JDeveloper ADF Faces Skin Editor, based on Shays KScope session http://t.co/KksU3vR and a blog http://t.co/KtdtbFG.

Wednesday
The day started with the third of my sessions "....and thus your forms automagically disappear", about considerations to make when you are about to decide whether or not to leave Forms and go for ADF. You will see the process of conversion step by step starting with the business case and ending at the new ADF application. You will hear about issues you run into when using Forms2ADF for automated conversion and you will get hints on how to use Forms2ADF.

Because of the cancellation of my "ADF Mobile client" session I helped out Steven Davelaar with organizing two JHeadstart Handson sessions. Besides that I had a great discussion with the 3 people that did show up for my cancelled ADF Mobile session about Mobile development with ADF. The handson session were pretty well visited and Steven Davelaar, Wilfred van der Deijl and me were around to help people out when needed.
Oracle JHeadstart: Superior Productivity in Developing Best-practice ADF Web Applications by Steven Davelaar of Oracle Corporation.
The day ended with an excellent party at the Queen Mary.

Thursday
The final day of KScope 11, but what a day that was. Thursday thunder was a huge succes. A bunch of FMW cracks (Peter Ebell, Edwin Biemond, Ronald van Luttikhuizen, Steven Davelaar and Luc Bors (thats me)) set down to do a 4 hour live development session using the Fusion Middleware toolstack. We build an ADF Dashboard to display statistics on KScope data, and an end to end process for submitting abstracts, using a queue to get an event into the business process, sending an email from the process to notify when the abstract is accepted, and finally uploading the presentation. The session was being moderated by Chris Muir, Lonneke Dikmans and Duncan Mills.

We were somewhat afraid of how the audience (if any) would respond to this session. Would there be an audience, and if so, would they stay, or would they leave after a few minutes. It turned out that we a good audience (I think some 75 people) and most of them returned after the one hour KScope Closing Session. In that session a personal succes for me was announced. It turned out that attendees of the Fusion Middleware Track choose me and one of my sessions to be rewarded the "KScope 2011 FMW track Best Speaker Award". I had to learn that via twitter, because I not attending that session. I was preparing the second part of the Thursday Thunder Session. All in all a great session and according to the blogosphere <http://debrasoracle.blogspot.com/2011/07/fmw-symposium-at-kscope.html> this will be repeated in some form at the UKOUG conference in December.

On my way back home I reviewed Grant Ronalds Whitepaper on Forms Modernization. The result can be found here.

KScope 2012
Next year KScope is in San Antonia TX from June 24th - June 28th. I'll be there.
Registration and abstract submission is now open. Find the links on http://kscope12.com/