| /* DocPrintJobImpl.java -- Implementation of DocPrintJob. |
| Copyright (C) 2006 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package gnu.javax.print.ipp; |
| |
| import gnu.javax.print.PrintFlavorException; |
| import gnu.javax.print.ipp.attribute.job.JobId; |
| import gnu.javax.print.ipp.attribute.job.JobUri; |
| import gnu.javax.print.ipp.attribute.printer.DocumentFormat; |
| import gnu.javax.print.ipp.attribute.supported.OperationsSupported; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.UnsupportedEncodingException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.print.CancelablePrintJob; |
| import javax.print.Doc; |
| import javax.print.DocFlavor; |
| import javax.print.DocPrintJob; |
| import javax.print.PrintException; |
| import javax.print.PrintService; |
| import javax.print.attribute.AttributeSetUtilities; |
| import javax.print.attribute.DocAttributeSet; |
| import javax.print.attribute.HashAttributeSet; |
| import javax.print.attribute.HashPrintJobAttributeSet; |
| import javax.print.attribute.PrintJobAttributeSet; |
| import javax.print.attribute.PrintRequestAttributeSet; |
| import javax.print.attribute.standard.JobName; |
| import javax.print.attribute.standard.PrinterURI; |
| import javax.print.attribute.standard.RequestingUserName; |
| import javax.print.event.PrintJobAttributeListener; |
| import javax.print.event.PrintJobEvent; |
| import javax.print.event.PrintJobListener; |
| |
| /** |
| * Implementation of the DocPrintJob interface. Implementation is |
| * specific to the <code>IppPrintService</code> implementation. |
| * |
| * @author Wolfgang Baer (WBaer@gmx.de) |
| */ |
| public class DocPrintJobImpl implements CancelablePrintJob |
| { |
| /** The print service this job is bound to. */ |
| private IppPrintService service; |
| |
| /** The set of print job listeners. */ |
| private HashSet printJobListener = new HashSet(); |
| |
| /** The print job attributes listeners. */ |
| private ArrayList attributesListener = new ArrayList(); |
| /** The print job attributes listeners associated attribute set. */ |
| private ArrayList attributesListenerAttributes = new ArrayList(); |
| |
| /** The username. */ |
| private String username; |
| /** The password of the user. */ |
| private String password; |
| |
| /** Returned job uri. */ |
| private JobUri jobUri = null; |
| /** Returned job id. */ |
| private JobId jobId = null; |
| |
| /** The requesting-username for later canceling */ |
| private RequestingUserName requestingUser; |
| |
| /** The print job sets. */ |
| private PrintJobAttributeSet oldSet = new HashPrintJobAttributeSet(); |
| private PrintJobAttributeSet currentSet = new HashPrintJobAttributeSet(); |
| |
| /** |
| * State variable if we already started printing. |
| */ |
| private boolean printing = false; |
| |
| // TODO Implement complete PrintJobListener notification |
| // TODO Implement PrintJobAttributeListener notification |
| |
| /** |
| * Constructs a DocPrintJobImpl instance bound to the given print service. |
| * |
| * @param service the print service instance. |
| * @param user the user of this print service. |
| * @param passwd the password of the user. |
| */ |
| public DocPrintJobImpl(IppPrintService service, String user, String passwd) |
| { |
| this.service = service; |
| username = user; |
| password = passwd; |
| } |
| |
| /** |
| * @see DocPrintJob#addPrintJobAttributeListener(PrintJobAttributeListener, PrintJobAttributeSet) |
| */ |
| public void addPrintJobAttributeListener(PrintJobAttributeListener listener, |
| PrintJobAttributeSet attributes) |
| { |
| if (listener == null) |
| return; |
| |
| attributesListener.add(listener); |
| attributesListenerAttributes.add(attributes); |
| } |
| |
| /** |
| * @see DocPrintJob#addPrintJobListener(PrintJobListener) |
| */ |
| public void addPrintJobListener(PrintJobListener listener) |
| { |
| if (listener == null) |
| return; |
| |
| printJobListener.add(listener); |
| } |
| |
| /** |
| * @see javax.print.DocPrintJob#getAttributes() |
| */ |
| public PrintJobAttributeSet getAttributes() |
| { |
| return AttributeSetUtilities.unmodifiableView(currentSet); |
| } |
| |
| /** |
| * @see javax.print.DocPrintJob#getPrintService() |
| */ |
| public PrintService getPrintService() |
| { |
| return service; |
| } |
| |
| /** |
| * @see DocPrintJob#print(Doc, PrintRequestAttributeSet) |
| */ |
| public void print(Doc doc, PrintRequestAttributeSet attributes) |
| throws PrintException |
| { |
| if (printing) |
| throw new PrintException("already printing"); |
| |
| printing = true; |
| |
| DocAttributeSet docAtts = doc.getAttributes(); |
| DocFlavor flavor = doc.getDocFlavor(); |
| |
| if (flavor == null || (!service.isDocFlavorSupported(flavor))) |
| { |
| notifyPrintJobListeners(new PrintJobEvent(this, PrintJobEvent.JOB_FAILED)); |
| throw new PrintFlavorException("Invalid flavor", new DocFlavor[] {flavor}); |
| } |
| |
| // merge attributes as doc attributes take precendence |
| // over the print request attributes |
| HashAttributeSet mergedAtts = new HashAttributeSet(); |
| |
| if (attributes != null) |
| mergedAtts.addAll(attributes); |
| if (docAtts != null) |
| mergedAtts.addAll(docAtts); |
| |
| // check for requesting-user-name -add the |
| // executing username if no other is specified |
| // save user name so we can make a cancel operation under same user |
| if (! mergedAtts.containsKey(RequestingUserName.class)) |
| { |
| mergedAtts.add(IppPrintService.REQUESTING_USER_NAME); |
| requestingUser = IppPrintService.REQUESTING_USER_NAME; |
| } |
| else |
| { |
| requestingUser = (RequestingUserName) |
| mergedAtts.get(RequestingUserName.class); |
| } |
| |
| // same for job-name |
| if (! mergedAtts.containsKey(JobName.class)) |
| mergedAtts.add(IppPrintService.JOB_NAME); |
| |
| IppResponse response = null; |
| |
| try |
| { |
| PrinterURI printerUri = service.getPrinterURI(); |
| String printerUriStr = "http" + printerUri.toString().substring(3); |
| |
| URI uri = null; |
| try |
| { |
| uri = new URI(printerUriStr); |
| } |
| catch (URISyntaxException e) |
| { |
| // does not happen |
| } |
| |
| IppRequest request = |
| new IppRequest(uri, username, password); |
| |
| request.setOperationID( (short) OperationsSupported.PRINT_JOB.getValue()); |
| request.setOperationAttributeDefaults(); |
| request.addOperationAttribute(printerUri); |
| |
| if (mergedAtts != null) |
| { |
| request.addAndFilterJobOperationAttributes(mergedAtts); |
| request.addAndFilterJobTemplateAttributes(mergedAtts); |
| } |
| |
| // DocFlavor getMimeType returns charset quoted |
| DocumentFormat format = DocumentFormat.createDocumentFormat(flavor); |
| request.addOperationAttribute(format); |
| |
| // Get and set the printdata based on the |
| // representation classname |
| String className = flavor.getRepresentationClassName(); |
| |
| if (className.equals("[B")) |
| { |
| request.setData((byte[]) doc.getPrintData()); |
| response = request.send(); |
| } |
| else if (className.equals("java.io.InputStream")) |
| { |
| InputStream stream = (InputStream) doc.getPrintData(); |
| request.setData(stream); |
| response = request.send(); |
| stream.close(); |
| } |
| else if (className.equals("[C")) |
| { |
| try |
| { |
| // CUPS only supports UTF-8 currently so we convert |
| // We also assume that char[] is always utf-16 - correct ? |
| String str = new String((char[]) doc.getPrintData()); |
| request.setData(str.getBytes("utf-16")); |
| response = request.send(); |
| } |
| catch (UnsupportedEncodingException e) |
| { |
| notifyPrintJobListeners(new PrintJobEvent(this, PrintJobEvent.JOB_FAILED)); |
| throw new PrintFlavorException("Invalid charset of flavor", e, new DocFlavor[] {flavor}); |
| } |
| } |
| else if (className.equals("java.io.Reader")) |
| { |
| try |
| { |
| // FIXME Implement |
| // Convert a Reader into a InputStream properly encoded |
| response = request.send(); |
| throw new UnsupportedEncodingException("not supported yet"); |
| } |
| catch (UnsupportedEncodingException e) |
| { |
| notifyPrintJobListeners(new PrintJobEvent(this, PrintJobEvent.JOB_FAILED)); |
| throw new PrintFlavorException("Invalid charset of flavor", e, new DocFlavor[] {flavor}); |
| } |
| } |
| else if (className.equals("java.lang.String")) |
| { |
| try |
| { |
| // CUPS only supports UTF-8 currently so we convert |
| // We also assume that String is always utf-16 - correct ? |
| String str = (String) doc.getPrintData(); |
| request.setData(str.getBytes("utf-16")); |
| response = request.send(); |
| } |
| catch (UnsupportedEncodingException e) |
| { |
| notifyPrintJobListeners(new PrintJobEvent(this, PrintJobEvent.JOB_FAILED)); |
| throw new PrintFlavorException("Invalid charset of flavor", e, new DocFlavor[] {flavor}); |
| } |
| } |
| else if (className.equals("java.net.URL")) |
| { |
| URL url = (URL) doc.getPrintData(); |
| InputStream stream = url.openStream(); |
| request.setData(stream); |
| response = request.send(); |
| stream.close(); |
| } |
| else if (className.equals("java.awt.image.renderable.RenderableImage") |
| || className.equals("java.awt.print.Printable") |
| || className.equals("java.awt.print.Pageable")) |
| { |
| // For the future :-) |
| throw new PrintException("Not yet supported."); |
| } |
| else |
| { |
| // should not happen - however |
| notifyPrintJobListeners(new PrintJobEvent(this, PrintJobEvent.JOB_FAILED)); |
| throw new PrintFlavorException("Invalid flavor", new DocFlavor[] {flavor}); |
| } |
| |
| // at this point the data is transfered |
| notifyPrintJobListeners(new PrintJobEvent( |
| this, PrintJobEvent.DATA_TRANSFER_COMPLETE)); |
| } |
| catch (IOException e) |
| { |
| throw new PrintException("IOException occured.", e); |
| } |
| |
| int status = response.getStatusCode(); |
| if (! (status == IppStatusCode.SUCCESSFUL_OK |
| || status == IppStatusCode.SUCCESSFUL_OK_IGNORED_OR_SUBSTITUED_ATTRIBUTES |
| || status == IppStatusCode.SUCCESSFUL_OK_CONFLICTING_ATTRIBUTES) ) |
| { |
| notifyPrintJobListeners(new PrintJobEvent( |
| this, PrintJobEvent.JOB_FAILED)); |
| throw new PrintException("Printing failed - received statuscode " + Integer.toHexString(status)); |
| |
| // TODO maybe specific status codes may require to throw a specific |
| // detailed attribute exception |
| } |
| else |
| { |
| // start print job progress monitoring thread |
| // FIXME Implement |
| |
| // for now we just notify as finished |
| notifyPrintJobListeners( |
| new PrintJobEvent(this, PrintJobEvent.JOB_COMPLETE)); |
| } |
| |
| List jobAtts = response.getJobAttributes(); |
| |
| // extract the uri and id of job for canceling and further monitoring |
| Map jobAttributes = (Map) jobAtts.get(0); |
| jobUri = (JobUri) ((HashSet)jobAttributes.get(JobUri.class)).toArray()[0]; |
| jobId = (JobId) ((HashSet)jobAttributes.get(JobId.class)).toArray()[0]; |
| } |
| |
| /** |
| * @see DocPrintJob#removePrintJobAttributeListener(PrintJobAttributeListener) |
| */ |
| public void removePrintJobAttributeListener(PrintJobAttributeListener listener) |
| { |
| if (listener == null) |
| return; |
| |
| int index = attributesListener.indexOf(listener); |
| if (index != -1) |
| { |
| attributesListener.remove(index); |
| attributesListenerAttributes.remove(index); |
| } |
| } |
| |
| /** |
| * @see DocPrintJob#removePrintJobListener(PrintJobListener) |
| */ |
| public void removePrintJobListener(PrintJobListener listener) |
| { |
| if (listener == null) |
| return; |
| |
| printJobListener.remove(listener); |
| } |
| |
| /** |
| * @see CancelablePrintJob#cancel() |
| */ |
| public void cancel() throws PrintException |
| { |
| if (jobUri == null) |
| { |
| throw new PrintException("print job is not yet send"); |
| } |
| |
| IppResponse response = null; |
| |
| try |
| { |
| IppRequest request = new IppRequest(jobUri.getURI(), username, password); |
| request.setOperationID( (short) OperationsSupported.CANCEL_JOB.getValue()); |
| request.setOperationAttributeDefaults(); |
| request.addOperationAttribute(jobUri); |
| request.addOperationAttribute(requestingUser); |
| response = request.send(); |
| } |
| catch (IOException e) |
| { |
| throw new IppException("IOException occured during cancel request.", e); |
| } |
| |
| int status = response.getStatusCode(); |
| if (! (status == IppStatusCode.SUCCESSFUL_OK |
| || status == IppStatusCode.SUCCESSFUL_OK_IGNORED_OR_SUBSTITUED_ATTRIBUTES |
| || status == IppStatusCode.SUCCESSFUL_OK_CONFLICTING_ATTRIBUTES) ) |
| { |
| notifyPrintJobListeners(new PrintJobEvent( |
| this, PrintJobEvent.JOB_FAILED)); |
| throw new PrintException("Canceling failed - received statuscode " + Integer.toHexString(status)); |
| } |
| else |
| { |
| notifyPrintJobListeners(new PrintJobEvent( |
| this, PrintJobEvent.JOB_CANCELED)); |
| } |
| } |
| |
| private void notifyPrintJobListeners(PrintJobEvent e) |
| { |
| Iterator it = printJobListener.iterator(); |
| while (it.hasNext()) |
| { |
| PrintJobListener l = (PrintJobListener) it.next(); |
| if (e.getPrintEventType() == PrintJobEvent.DATA_TRANSFER_COMPLETE) |
| l.printDataTransferCompleted(e); |
| else if (e.getPrintEventType() == PrintJobEvent.JOB_CANCELED) |
| l.printJobCanceled(e); |
| else if (e.getPrintEventType() == PrintJobEvent.JOB_COMPLETE) |
| l.printJobCompleted(e); |
| else if (e.getPrintEventType() == PrintJobEvent.JOB_FAILED) |
| l.printJobFailed(e); |
| else if (e.getPrintEventType() == PrintJobEvent.NO_MORE_EVENTS) |
| l.printJobNoMoreEvents(e); |
| else |
| l.printJobRequiresAttention(e); |
| } |
| } |
| |
| } |