Skip to main content

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.

Comments

Gieofkkskfofd said…
Interesting post. However you seem to suggest this is printing from the client. Isn't this printing from the server and therefor showing printers available on the server, not the client. Can still be very helpful though
Luc Bors said…
Thanks Wilfred. I adjusted the post. Client is gone where it might raise the wrong suggestion.
Amazing post..ADF 11G printing is really amazing..Its mind blowing..Thanks for telling about 3D printing.
adelaide printer
Luc Bors said…
On behalve of Rakesh

Hi Sir,

I am a beginner in ADF and have been trying your excellent post on ADF: Print Directly from your Application.

Can you please send me the workspace please ?

Rakesh

From Mauritius Island.
Luc Bors said…
requested by Wilhelm Colln
Hello Luc,

I have a requirement to print about 1000 documents (letters)
to a local pinter with information produced from a template (html)
with custom tagsassociated to information from de Oracle DB.

I think part of your example will help us to handle the selection of the printer.
Could you share your workspace ?

Any hint about working around this requirement would be appreciated.

I have made some tests and to avoid printing the header/footer of the browser
used the pdf format, but it is a slow process.

Thanks in advance,

Wilhelm
Luc Bors said…
Requested by Eduardo D. Vidal

Hello, I was reading your blog entry http://lucbors.blogspot.com/2011/07/adf-11g-printing-directly-from-your.html
and I would like to request the workspace you used for the sample, right know I'm working on something related with direct printing and your post really helps me.

Receive my regards.
Luc Bors said…
Requested by Eduardo D. Vidal

Hello, I was reading your blog entry http://lucbors.blogspot.com/2011/07/adf-11g-printing-directly-from-your.html
and I would like to request the workspace you used for the sample, right know I'm working on something related with direct printing and your post really helps me.

Receive my regards.
Unknown said…
Excellent solution, thank you very much,could you share to me work space
julykt@gmai.com

Regards
KT
Iván said…
I'm having troubles when the application is deployed in an i-frame in other adf application.
It returns a null value.
Any idea how can I solve this problem?
Your approach is good till the client and application server run on the same host. Otherwise you should use applets. look at this post http://adfpractice-sokol.blogspot.com/2012/06/obtaining-hostname-and-printers-via.html
Naeem Qasim said…
Hi, This is a very good article. In this article, you are printing files present on the hard drive. I have a requirement to print the data shown on the web page using an ADF read-only table. How can I print this data.
Luc Bors said…
Hi Naeem, you can use showprintablepageBehavior tag for that.
Anonymous said…
Hi I have tried this code this code is working fine for hp lazer printers but it is not sending any print to thermal printers and snagit printers please give me any suggestion what modifications are required
Hi.... good post.. Helped me a lot.. but is there any possibility to pass a query and query result printed. suggest me a link..
Vishnu said…
HI Luc,

Thanks for the post.I used this application and tried to print, but its not working
neither am i getting any error.
I am using jdev 11.1.2.0.0

Please help me.

Thanks,
Vishnu


Gaurav Kishan said…
Hi,

I tried running you application, but I couldn't print the document.

Is there any extra configuration I need to do for printing to work from Application ?

Regards,
Gaurav Kishan
Gaurav Kishan said…
Hi,

I tried running you application, but I couldn't print the document.

Is there any extra configuration I need to do for printing to work from Application ?

Regards,
Gaurav Kishan
Gaurav Kishan said…
Hi,

I tried running you application, but I couldn't print the document.

Is there any extra configuration I need to do for printing to work from Application ?

Regards,
Gaurav Kishan

Popular posts from this blog

ADF 12.1.3 : Implementing Default Table Filter Values

In one of my projects I ran into a requirement where the end user needs to be presented with default values in the table filters. This sounds like it is a common requirement, which is easy to implement. However it proved to be not so common, as it is not in the documentation nor are there any Blogpost to be found that talk about this feature. In this blogpost I describe how to implement this. The Use Case Explained Users of the application would typically enter today's date in a table filter in order to get all data that is valid for today. They do this each and every time. In order to facilitate them I want to have the table filter pre-filled with today's date (at the moment of writing July 31st 2015). So whenever the page is displayed, it should display 'today' in the table filter and execute the query accordingly. The problem is to get the value in the filter without the user typing it. Lets first take a look at how the ADF Search and Filters are implemented by

ADF 11g Quicky 3 : Adding Error, Info and Warning messages

How can we add a message programatically ? Last week I got this question for the second time in a months time. I decided to write a short blogpost on how this works. Adding messages is very easy, you just need to know how it works. You can add a message to your faces context by creating a new FacesMessage. Set the severity (ERROR, WARNING, INFO or FATAL ), set the message text, and if nessecary a message detail. The fragment below shows the code for an ERROR message. 1: public void setMessagesErr(ActionEvent actionEvent) { 2: String msg = "This is a message"; 3: AdfFacesContext adfFacesContext = null; 4: adfFacesContext = AdfFacesContext.getCurrentInstance(); 5: FacesContext ctx = FacesContext.getCurrentInstance(); 6: FacesMessage fm = 7: new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, ""); 8: ctx.addMessage(null, fm); 9: } I created a simple page with a couple of buttons to show the result of setting the message. When the but

How to: Adding Speech to Oracle Digital Assistant; Talk to me Goose

At Oracle Code One in October, and also on DOAG in Nurnberg Germany in November I presented on how to go beyond your regular chatbot. This presentation contained a part on exposing your Oracle Digital Assistant over Alexa and also a part on face recognition. I finally found the time to blog about it. In this blogpost I will share details of the Alexa implementation in this solution. Typically there are 3 area's of interest which I will explain. Webhook Code to enable communication between Alexa and Oracle Digital Assistant Alexa Digital Assistant (DA) Explaining the Webhook Code The overall setup contains of Alexa, a NodeJS webhook and an Oracle Digital Assistant. The webhook code will be responsible for receiving and transforming the JSON payload from the Alexa request. The transformed will be sent to a webhook configured on Oracle DA. The DA will send its response back to the webhook, which will transform into a format that can be used by an Alexa device. To code