Demo: Print Server Using the Java[tm] Print Service API
on the Sun[tm] ONE Application Server
by Rajesh Ramchandani
(April, 2003)
We want to hear from you! Please send us your
FEEDBACK.
The following software tool may contain actual software programs in source code form.
This source code is made available for developers to use as needed, pursuant to the
terms and conditions of this license.
Summary:
This document demonstrates the design and development of a Print Server application
on the Sun[tm] One Application Server. Java[tm] SDK 1.4 introduced a new set of
Unified Print APIs that uses the extendable, industry standard
attribute set specified in the Internet
Printing Protocol (IPP) 1.1 from the IETF (Internet Engineering Task
Force). We will use the Java[tm] Print Service ("JPS") API to develop a Web
application and deploy it on the Sun ONE Application Server.
Readme click here
Download Source
here
Document Structure:
Overview of the Java[tm]
Print Service API
Java[tm] Print Service APIs are based on the proposed Unified
Printing Model in Java[tm] Specification Request ("JSR") 6. The Unified Printing Model will allow
printing on all platforms requiring a Java Print Service API, including platforms
requiring a small footprint, such as a Java[tm] 2 Platform, Micro Edition
(J2ME[tm] Platform), but will still support the current Java[tm] 2 software Print API,
which allows printing of all Java 2D[tm] API graphics.
The JPS API maintains backward compatibility with
the previous Java[tm] programming language AWT Print API, so applications written to print
2D graphics, text and images can still print using the new JPS API.
Some of the interesting features that Java Print Service APIs offer
are:
-
Printer Discovery: Both the client and server side applications
can programmatically handle the Printer Discovery and find suitable printers
that can print the Print Job with user-specified Print Job attributes.
There are several Print Job attributes that one can specify. For example,
number of sides for printing - single-sided or double-sided, Chromaticity
(Color or Monochrome), Media size (A4, legal, letter etc.), Print Job name
(specify a name for any printing job) etc.
-
Print Job Attributes as Objects: Implementations of standard IPP
attributes are included in the JPS API as objects.
-
Print Job Attribute Classes: Applications can extend the attributes
included with the JPS API.
-
Service Provider Interface: Third parties can plug in their
own Print Services using the Service Provider Interfaces. Using the vendor-provided SPI,
Print Service can be implemented most effectively
and efficiently.
Java Print Service
Architecture
JPS APIs are provided in the following packages:
javax.print
This is the main JPS API package which provides the following functionality:
JPS provides an API like PrintServiceLookup() to look up printers
that can satisfy all the requested attributes for the Print Job. For example,
if the requested Print Job is a GIF image file and needs to be printed
on A4 size paper and in color, then, PrintServiceLookup() is used to search
for the relevant printers that can satisfy all if not most of the
requested attributes of the Print Job, instead of just sending the print
job to any printer. If the Fidelity attribute is set to true, then only the
Print Services that can satisfy all the Print Job attributes will be returned.
The javax.print package provides a StreamPrintService API,
to send print data to an output stream which an application can use to
convert print data from one format to other. For example, use
the StreamPrintService API to send 2D graphics images to an output
stream.
-
Specify the Print Data Format
JPS provides the DocFlavor class to specify what print data
format is associated with the Print Service. The print data format includes
JPEG image, GIF image, text file, HTML file, PDF file, Postscript file,
etc. There are APIs to discover what data formats a Print Service or a printer
can print directly, instead of using another software to convert the print
data to a format the printer understands. For example, a printer may be
able to print PDF files or image files directly when sent from an InputStream.
DocFlavor can handle client-formatted print data (that is, the user
knows the type of the file to be printed) or service-formatted print
data (that is, let the Print Service decide, and interpret the print data format).
DocFlavor requires two parameters:
-
A MIME type which tells the Print Service how to interpret the print data.
For example, to print an HTML page, the MIME type is set to "text/html;
charset=utf-16"
.
-
A representation Java programming language class indicating to the Print Service how the print
data is sent to the printer. For example, class
java.io.InputStream can
be specified if the print data is sent to the Print Service through a stream .
It is always a good idea to query the Print Service for the supported print
data format of DocFlavors before sending the print data to a Print Service.
Once a suitable Print Service is located with the PrintServiceLookup()
method using the set of requested attributes, a Print Job is created by
the DocPrintJob class from the Print Service. This Print Job is then sent
to the printer for printing. The DocPrintJob class provides the print method
for sending the Print Job to the printer. To print 2D graphics JPS
provides the PrinterJob class that can be utilized by an application.
javax.print.attribute
and javx.print.attribute.standard
These two JPS packages help to specify Print Job attributes, print request
attributes and Document attributes. Let's look at each of these attribute
sets below:
Print Request attributes are the attributes an application
specifies, describing characteristics of the whole Print Job and
all the docs in the Print Job. For example, number of copies for a print
job, Chromaticity or number of sides to print on.
Each of the attributes that can be applied to the print request must
implement the PrintRequestAttribute interface. For example, class
Copies implements PrintRequestAttribute and provides some
constants like SINGLE_DOCUMENT, SINGLE_DOCUMENT_NEW_SHEET, etc. describing the number
of copies to print, whether to use new sheets or to collate them.
The set of print request attributes (like any other attributes) can
be applied to a Print Job using a Hash implementation of
PrintRequestAttributeSet, that is, HashPrintRequestAttributeSet
.
Print job attributes report the status of the Print Job or some other characteristics
of the print Job. Print Service uses PrintJobAttributeSet to report the
status of the Print Job. For example, JobState. JobState is
a printing attribute class, an enumeration, that identifies the current state of a
Print Job. Class JobState defines standard job state values. Now however,
the Print Service does not have to report all the PrintJob attributes.
Print Service attributes report the status of Print Service or some other
characteristics of Print Service. A Print Service instance adds a number
of PrintServiceAttributes to a Print Service's
attribute set to report the Print Service's status. For example,
PagesPerMinute class reports the number of pages a printer can print
nominally. This is a just an informative number; it is not a guarantee.
Class PrinterState provides an enumeration describing the current
state of the printer.
DocAttributeSet specifies the characertistics of the individual document and
Print Job settings to be applied to an individual document or print data. For
example, class PrintQuality is an enumeration that specifies the print
quality used by the printer. The print quality specified can be DRAFT (lowest
print quality , HIGH (highest print quality ), NORMAL (normal print quality).
javax.print.event
JPS contains classes allowing the applications to register
for events on the Print Jobs (PrintJobAttributeSet) and Print Service
(PrintServiceAttributeSet). The event listener model is the same
as the AWT event listener's model.
-
Print Service event listener
An application can use the addPrintServiceAttributeListener() method to
add a listener to the Print Service and monitor the status changes in the Print
Service attributes.
The PrintServiceAttributeListener.attributeUpdate() method is called
when Print Service attributes change. The service uses the
PrintServiceAttributeListener interface to decide which events it supports.
Inside the attributeUpdate() method, the client application can determine
which Print Service (getPrintService)
triggered the event, and can also determine the set of a Print Service attributes that
changed (PrintServiceAttributeSet).
Client applications can use PrintJobAttributeListener or
PrintJobListener
to monitor changes in the Print Job attributes. These listeners are installed
on DocPrintJob and one may be preferred over the other, depending on the
events to be monitored.
-
PrintJobAttributeListener
Class PrintJobAttributeEvent encapsulates an event a Print Service
reports to let the client know that one or more printing attributes for
a Print Job have changed. One example of this event being triggered
is when the JobState attribute changes from PROCESSING to
PROCESSING_STOPPED.
This event listener is easier to use than PrintJobAttributeListener,
as it delivers higher level messages, unlike the PrintJobAttributeListener.
These messages contain details on individual attributes and may not be
very useful to the client application. For example, a client application
is more interested in knowing the progress of a Print Job sent to the printer.
The PrintJobListener interface provides methods like
printDataTransferCompleted(),
printJobFailed(), etc. which can be implemented in the client application
to report and take appropriate actions on receiving events from the DocPrintJob.
Sample
Web Application - JPS Print Server
Now that we have seen the overview of JPS and are familiar
with the JPS architecture, let's take a look at how these APIs can
be used to write an application. We will discuss the code here. You can download
the source code for Print
Servlet here. The Servlet prints out the names of the methods
and the respective JPS API when the code executes. Please read the
README file provided with the source code for the instructions
on how to set up Sun ONE Application Server and other required jar
files to run this demo application.
Let's get familiar with the steps required to send a Print Job
to the Print Service or to a printer.
-
Specify a
DocFlavor type depending on the file type that is to be printed.
The DocFlavor can be specified by the client when the file type is
known, or it can be service-formatted DocFlavor which will depend on the
Print Service to determine the file type or print data type. In our sample
Print application, we will specify the DocFlavor type and use text file
for printing. Although, it would be a good idea to query the Print Service
for the supported DocFlavors before sending the print data.
-
Build an attribute set, typically,
PrintRequestAttributeSet to cite
the user-specified Print Job attributes. In our sample application, we
specify the default PrintRequest attributes in the deployment descriptor
(web.xml) which will be applied in case the user doesn't specifically select any
attributes.
-
Discover the Print Services that satisfy the requested attribute
set of the specified
DocFlavor. In our sample Print application, we use
the compromiseAttribute() method, which eliminates one attribute at a time
from the user-specified attribute set, until we find an appropriate Print
Service. In case there are no Print Services that can satisfy the user-specified
attributes, the client exits with the message "No Printers Found".
-
Add any Print Service or Print Job Listeners to the Print Service(s) depending
on what events the application is interested in.
-
Create a Print Job or doc from the Print Service
.
-
Finally, call the
print() method on the DocPrintJob to send
the print data to the printer.
Let's look at the source code for doPost() in our sample Print application.
//Obtain the Print Job Requested Attributes
p.setFidelity(request.getParameter("fidelity"));
p.setFilename(request.getParameter("filename"));
p.setChromaticity(request.getParameter("chromaticity"));
p.setCopies(request.getParameter("copies"));
p.setDestination(request.getParameter("destination"));
p.setJobname(request.getParameter("jobname"));
p.setJobsheets(request.getParameter("jobsheets"));
p.setMedia(request.getParameter("media"));
p.setFiletypestr(request.getParameter("filetype"));
p.setOrientation(request.getParameter("orient"));
p.setSides(request.getParameter("sides"));
p.setMediasize(request.getParameter("mediasize"));
p.setRequest(); //All Attributes are in place
// Let's see if the specified file type is a supported
// file type
if(!(p.isSupportedFileType(filetypestr))) {
System.out.println("File Type is not supported");
System.exit(1);
}
//Calling this method sets the appropriate DocFlavor.
// In case of text file it is set to
//DocFlavor.INPUT_STREAM.TEXT_PLAIN_US_ASCII;
p.setDocFlavor();
//Let's get a list of Print Services that satisfy our request
//attributes for given DocFlavor
PrintService[] ps = p.getPrintServices();
if(ps.length == 0) {
System.out.println("No printers found, exiting!");
System.exit(1);
}
//Let's query the Print Service to see what DocFlavors
//are supported
DocFlavor[] sdf = ps[0].getSupportedDocFlavors();
System.out.println("Supported DocFlavors are: ");
for( int r = 0; r < sdf.length; r++ ) {
System.out.println(sdf[r].toString());
}
//Let's add some PrintServiceAttributeListener and monitor
// the events.
p.addPrintAListener(ps);
//Let's print all the Print Services we discovered
PrintService[] plist = p.listPrinters(); //List of All
//print Services available
System.out.println("Printers are: ");
for(int i=0; i < plist.length; i++ ) {
System.out.println(plist[i]);
}
//Let's print the Default Print Service configured
PrintService defp = p.getDefaultPrintService(); //Default
//Print Service if you want to use
System.out.println("Default printer is " + defp.getName()
);
//Create a Print Job from the selected print Service
DocPrintJob pj = p.createDocPrintJob(ps[0]);
//Add a PrintJobAttrubuteListener to the selected Print
//Service and monitor print progress
p.addPrintJListener(pj);
//Create a Doc object before calling print()
Doc d = p.createFinalDoc();
//Print the Doc to the selected Print Service
printJob(pj,d);
}
The .props file specified the following attributes
for the Print Job:
CHROMATICITY:monochrome
COPIES:1
DESTINATION:
FIDELITY:false
JOBNAME:Java Printing Servlet
JOBSHEETS:standard
MEDIA:iso-a4
MEDIAPRINTABLEAREA:
FILETYPE:text
FILENAME:file.txt
MEDIA:A4
SIDES:DUPLEX
ORIENTATION:Potrait
FILENAME:file.txt
Here's the output from the Print application (stripped to show only
relevant information):
File Type is text
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
javax.print.attribute.standard.OrientationRequested
Printer name is: pomona
Supported Print Service attribute categories are:
class javax.print.attribute.standard.Chromaticity
Color, Monochrome
class javax.print.attribute.standard.Copies
1-1000
class javax.print.attribute.standard.Destination
file:out.ps
class javax.print.attribute.standard.Fidelity
true
class javax.print.attribute.standard.JobName
Java Printing
class javax.print.attribute.standard.JobSheets
, standard
class javax.print.attribute.standard.Media
, tabloid, ledger, na-legal, executive,
iso-a3, iso-a4, iso-a5, iso-b4, iso-b5class javax.print.attribute.standard.MediaPrintableArea
Printer name is: pomona
Supported DocFlavors are:
application/postscript; class="[B"
application/postscript; class="java.io.InputStream"
application/postscript; class="java.net.URL"
image/gif; class="[B"
image/gif; class="java.io.InputStream"
image/gif; class="java.net.URL"
image/jpeg; class="[B"
image/jpeg; class="java.io.InputStream"
image/jpeg; class="java.net.URL"
image/png; class="[B"
image/png; class="java.io.InputStream"
image/png; class="java.net.URL"
text/plain; charset="utf-16"; class="[C"
text/plain; charset="utf-16"; class="java.io.Reader"
text/plain; charset="utf-16"; class="java.lang.String"
text/plain; charset="iso646-us"; class="[B"
text/plain; charset="utf-8"; class="[B"
text/plain; charset="utf-16"; class="[B"
text/plain; charset="utf-16be"; class="[B"
text/plain; charset="utf-16le"; class="[B"
text/plain; charset="us-ascii"; class="[B"
text/plain; charset="iso646-us"; class="java.io.InputStream"
text/plain; charset="utf-8"; class="java.io.InputStream"
text/plain; charset="utf-16"; class="java.io.InputStream"
text/plain; charset="utf-16be"; class="java.io.InputStream"
text/plain; charset="utf-16le"; class="java.io.InputStream"
text/plain; charset="us-ascii"; class="java.io.InputStream"
text/plain; charset="iso646-us"; class="java.net.URL"
text/plain; charset="utf-8"; class="java.net.URL"
text/plain; charset="utf-16"; class="java.net.URL"
text/plain; charset="utf-16be"; class="java.net.URL"
text/plain; charset="utf-16le"; class="java.net.URL"
text/plain; charset="us-ascii"; class="java.net.URL"
application/x-java-jvm-local-objectref; class="java.awt.print.Pageable"
application/x-java-jvm-local-objectref; class="java.awt.print.Printable"
application/octet-stream; class="[B"
application/octet-stream; class="java.net.URL"
application/octet-stream; class="java.io.InputStream"
No. of print Services Found is : 1
Printers are:
Unix Printer : pomona
Default Print service is Unix Printer : pomona
Default printer is pomona
Data xfer completed !
All events DONE
Now let's look at some of the important methods in our Print application.
The Javadoc[tm] for Print application is available
here:
The first step is to set the DocFlavor depending on the type of
file we are printing. In Print application, we check the FILETYPE property
and create the DocFlavor in setDocFlavor() method.
/**
* Sets Document Flavor Type (DocFlavor) which will be used by
* PrintServiceLookup and also to create a Doc for Print Service.
* @param none
* @return void
*/
public void setDocFlavor() { <.code>
--
--
--
if (filetypestr.equalsIgnoreCase( "text") || filetypestr.equalsIgnoreCase(
"txt")) {
myFlavor = DocFlavor.INPUT_STREAM.TEXT_PLAIN_US_ASCII;
}
if (filetypestr.equalsIgnoreCase( "html")) {
System.out.println("setDocFlaovr is setting HTML....");
myFlavor = DocFlavor.INPUT_STREAM.TEXT_HTML_US_ASCII;
}
--
--
--
The next step is to create an attribute set from the user-requested print
job attributes. We do this in the getDocProperties() and the
setRequest() methods. The getDocProperties() method reads
the .props file and setRequest() adds the requested attributes to the
HashPrintrequestAttributeSet. The
HashPrintRequestAttributeSet is passed on to PrintServiceLookup()
to discover the suitable Print Services.
/**
* Creates the
request for print service with specified attributes
*/
public void setRequest()
{
aset = new HashPrintRequestAttributeSet()'
if (chromaticity !=
null ) {
if(chromaticity.equalsIgnoreCase( "monochrome")) {
aset.add(Chromaticity.MONOCHROME);
}
if(chromaticity.equalsIgnoreCase( "color")) {
aset.add(Chromaticity.COLOR);
}
} else {
aset.add(Chromaticity.MONOCHROME);
}
/* Set number of Copies */
if(copies == null ) {
aset.add(new Copies(1));
}
if (copies != null ) {
aset.add(new Copies(Integer.parseInt(copies)));
}
--
--
--
The third step is to discover the Print Services that can satisfy our
PrintRequestAttributeSet
for the chosen DocFlavor. In our Print application, we implement a
compromiseAttribute() method to compromise one attribute from the
HashPrintrequestAttributeSet() until we find some Print Service. The
compromiseAttribute() method removes
one attribute at a time from the user-specified attribute set until a suitable
Print Service is discovered. The logic used in this method is very
simple and may not be satisfactory for some applications.
We use the getPrintServices() method in our Print application.
--
--
services = PrintServiceLookup.lookupPrintServices(myFlavor, aset);
if(services.length > 0 ) {
break;
}else {
System.out.println(" No Print Services found, will compromise attribute");
compromiseAttribute(i); //remove one attribute
}
--
--
--
Once we discover the Print Services, we can install Print Service and
Print Job Attribute Listeners.
/**
* Add PrintAttributeListener
to the service
* This monitors the
changes in Print Service Attributes
* @param
Print service
* @return
void
*/
public void addPrintAListener(PrintService
service) {
PrintAListener pAListener = new PrintAListener();
service.addPrintServiceAttributeListener(pAListener);
}
/**
* Add PrintAttributeListener
to the all Print services
* This monitors the
changes in Print Service Attributes
* @param
array of print services
* @return
void
*/
public void addPrintAListener(PrintService[]
service) {
PrintAListener pAListener = new PrintAListener();
for(int i = 0; i < service.length; i++) {
System.out.println("Adding AListener to service number: " + i);
service[i].addPrintServiceAttributeListener(pAListener);
}
}
The fourth step is to create a Print Job from the selected Print Service.
This is how we do it in our sample Print application:
/**
* Creates a Print
Job from one of the Print Services
* @param
printservice (usually returned from getPrintServices()
* @return
DocprintJob
*/
public DocPrintJob createDocPrintJob(PrintService
ps) {
DocPrintJob job = ps.createPrintJob();
return job;
}
And finally, create a Doc object and send it to the Print Service using
print() method of DocPrintJob.
/**
* Create a Doc for
printing. Will use SimpleDoc for now
* @param
none
* @return
Doc
*/
public Doc createFinalDoc()
{
System.out.println(" parameters are : "+ fin + myFlavor );
myDoc = new SimpleDoc(fin, myFlavor, null);
return myDoc;
}
/**
* Prints the Print
Job to the specified printService
* @param
DocPrintJob
*
Doc
* @return
void
*/
public static void printJob(DocPrintJob
dpj, Doc d) {
DocPrintJob ljob = dpj;
Doc ld = d;
try {
--
--
--
ljob.print(ld,aset);
System.out.println("printed ljob");
--
--
}catch(PrintException pe) {
System.out.println("Got Printer Exception pe:" +pe.getMessage());
System.out.println("Cause of pe is " + pe.getCause());
pe.printStackTrace();
--
--
--
What's More on JPS:
What this document and sample Print application do not cover is printing
2D graphics and using Service-formatted print data. Please refer
to the JPS User's Guide for examples on how to work with these two items.
Excellent References for JPS:
|