Source for gnu.javax.print.ipp.DocPrintJobImpl

   1: /* DocPrintJobImpl.java -- Implementation of DocPrintJob.
   2:    Copyright (C) 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.javax.print.ipp;
  40: 
  41: import gnu.javax.print.PrintFlavorException;
  42: import gnu.javax.print.ipp.attribute.job.JobId;
  43: import gnu.javax.print.ipp.attribute.job.JobUri;
  44: import gnu.javax.print.ipp.attribute.printer.DocumentFormat;
  45: import gnu.javax.print.ipp.attribute.supported.OperationsSupported;
  46: 
  47: import java.io.IOException;
  48: import java.io.InputStream;
  49: import java.io.UnsupportedEncodingException;
  50: import java.net.URI;
  51: import java.net.URISyntaxException;
  52: import java.net.URL;
  53: import java.util.ArrayList;
  54: import java.util.HashSet;
  55: import java.util.Iterator;
  56: import java.util.List;
  57: import java.util.Map;
  58: 
  59: import javax.print.CancelablePrintJob;
  60: import javax.print.Doc;
  61: import javax.print.DocFlavor;
  62: import javax.print.DocPrintJob;
  63: import javax.print.PrintException;
  64: import javax.print.PrintService;
  65: import javax.print.attribute.AttributeSetUtilities;
  66: import javax.print.attribute.DocAttributeSet;
  67: import javax.print.attribute.HashAttributeSet;
  68: import javax.print.attribute.HashPrintJobAttributeSet;
  69: import javax.print.attribute.PrintJobAttributeSet;
  70: import javax.print.attribute.PrintRequestAttributeSet;
  71: import javax.print.attribute.standard.JobName;
  72: import javax.print.attribute.standard.PrinterURI;
  73: import javax.print.attribute.standard.RequestingUserName;
  74: import javax.print.event.PrintJobAttributeListener;
  75: import javax.print.event.PrintJobEvent;
  76: import javax.print.event.PrintJobListener;
  77: 
  78: /**
  79:  * Implementation of the DocPrintJob interface. Implementation is
  80:  * specific to the <code>IppPrintService</code> implementation.
  81:  *
  82:  * @author Wolfgang Baer (WBaer@gmx.de)
  83:  */
  84: public class DocPrintJobImpl implements CancelablePrintJob
  85: {
  86:   /** The print service this job is bound to. */
  87:   private IppPrintService service;
  88: 
  89:   /** The set of print job listeners. */
  90:   private HashSet printJobListener = new HashSet();
  91: 
  92:   /** The print job attributes listeners. */
  93:   private ArrayList attributesListener = new ArrayList();
  94:   /** The print job attributes listeners associated attribute set. */
  95:   private ArrayList attributesListenerAttributes = new ArrayList();
  96: 
  97:   /** The username. */
  98:   private String username;
  99:   /** The password of the user. */
 100:   private String password;
 101: 
 102:   /** Returned job uri. */
 103:   private JobUri jobUri = null;
 104:   /** Returned job id. */
 105:   private JobId jobId = null;
 106: 
 107:   /** The requesting-username for later canceling */
 108:   private RequestingUserName requestingUser;
 109: 
 110:   /** The print job sets. */
 111:   private PrintJobAttributeSet oldSet = new HashPrintJobAttributeSet();
 112:   private PrintJobAttributeSet currentSet = new HashPrintJobAttributeSet();
 113: 
 114:   /**
 115:    * State variable if we already started printing.
 116:    */
 117:   private boolean printing = false;
 118: 
 119:   // TODO Implement complete PrintJobListener notification
 120:   // TODO Implement PrintJobAttributeListener notification
 121: 
 122:   /**
 123:    * Constructs a DocPrintJobImpl instance bound to the given print service.
 124:    *
 125:    * @param service the print service instance.
 126:    * @param user the user of this print service.
 127:    * @param passwd the password of the user.
 128:    */
 129:   public DocPrintJobImpl(IppPrintService service, String user, String passwd)
 130:   {
 131:     this.service = service;
 132:     username = user;
 133:     password = passwd;
 134:   }
 135: 
 136:   /**
 137:    * @see DocPrintJob#addPrintJobAttributeListener(PrintJobAttributeListener, PrintJobAttributeSet)
 138:    */
 139:   public void addPrintJobAttributeListener(PrintJobAttributeListener listener,
 140:       PrintJobAttributeSet attributes)
 141:   {
 142:     if (listener == null)
 143:       return;
 144: 
 145:     attributesListener.add(listener);
 146:     attributesListenerAttributes.add(attributes);
 147:   }
 148: 
 149:   /**
 150:    * @see DocPrintJob#addPrintJobListener(PrintJobListener)
 151:    */
 152:   public void addPrintJobListener(PrintJobListener listener)
 153:   {
 154:     if (listener == null)
 155:       return;
 156: 
 157:     printJobListener.add(listener);
 158:   }
 159: 
 160:   /**
 161:    * @see javax.print.DocPrintJob#getAttributes()
 162:    */
 163:   public PrintJobAttributeSet getAttributes()
 164:   {
 165:     return AttributeSetUtilities.unmodifiableView(currentSet);
 166:   }
 167: 
 168:   /**
 169:    * @see javax.print.DocPrintJob#getPrintService()
 170:    */
 171:   public PrintService getPrintService()
 172:   {
 173:     return service;
 174:   }
 175: 
 176:   /**
 177:    * @see DocPrintJob#print(Doc, PrintRequestAttributeSet)
 178:    */
 179:   public void print(Doc doc, PrintRequestAttributeSet attributes)
 180:       throws PrintException
 181:   {
 182:     if (printing)
 183:       throw new PrintException("already printing");
 184: 
 185:     printing = true;
 186: 
 187:     DocAttributeSet docAtts = doc.getAttributes();
 188:     DocFlavor flavor = doc.getDocFlavor();
 189: 
 190:     if (flavor == null || (!service.isDocFlavorSupported(flavor)))
 191:       {
 192:         notifyPrintJobListeners(new PrintJobEvent(this, PrintJobEvent.JOB_FAILED));
 193:         throw new PrintFlavorException("Invalid flavor", new DocFlavor[] {flavor});
 194:       }
 195: 
 196:     // merge attributes as doc attributes take precendence
 197:     // over the print request attributes
 198:     HashAttributeSet mergedAtts = new HashAttributeSet();
 199: 
 200:     if (attributes != null)
 201:       mergedAtts.addAll(attributes);
 202:     if (docAtts != null)
 203:       mergedAtts.addAll(docAtts);
 204: 
 205:     // check for requesting-user-name -add the
 206:     // executing username if no other is specified
 207:     // save user name so we can make a cancel operation under same user
 208:     if (! mergedAtts.containsKey(RequestingUserName.class))
 209:       {
 210:         mergedAtts.add(IppPrintService.REQUESTING_USER_NAME);
 211:         requestingUser = IppPrintService.REQUESTING_USER_NAME;
 212:       }
 213:     else
 214:       {
 215:         requestingUser = (RequestingUserName)
 216:           mergedAtts.get(RequestingUserName.class);
 217:       }
 218: 
 219:     // same for job-name
 220:     if (! mergedAtts.containsKey(JobName.class))
 221:       mergedAtts.add(IppPrintService.JOB_NAME);
 222: 
 223:     IppResponse response = null;
 224: 
 225:     try
 226:       {
 227:         PrinterURI printerUri = service.getPrinterURI();
 228:         String printerUriStr = "http" + printerUri.toString().substring(3);
 229: 
 230:         URI uri = null;
 231:         try
 232:           {
 233:             uri = new URI(printerUriStr);
 234:           }
 235:         catch (URISyntaxException e)
 236:           {
 237:             // does not happen
 238:           }
 239: 
 240:         IppRequest request =
 241:           new IppRequest(uri, username, password);
 242: 
 243:         request.setOperationID( (short) OperationsSupported.PRINT_JOB.getValue());
 244:         request.setOperationAttributeDefaults();
 245:         request.addOperationAttribute(printerUri);
 246: 
 247:         if (mergedAtts != null)
 248:           {
 249:              request.addAndFilterJobOperationAttributes(mergedAtts);
 250:              request.addAndFilterJobTemplateAttributes(mergedAtts);
 251:           }
 252: 
 253:         // DocFlavor getMimeType returns charset quoted
 254:         DocumentFormat format = DocumentFormat.createDocumentFormat(flavor);
 255:         request.addOperationAttribute(format);
 256: 
 257:         // Get and set the printdata based on the
 258:         // representation classname
 259:         String className = flavor.getRepresentationClassName();
 260: 
 261:         if (className.equals("[B"))
 262:           {
 263:             request.setData((byte[]) doc.getPrintData());
 264:             response = request.send();
 265:           }
 266:         else if (className.equals("java.io.InputStream"))
 267:           {
 268:             InputStream stream = (InputStream) doc.getPrintData();
 269:             request.setData(stream);
 270:             response = request.send();
 271:             stream.close();
 272:           }
 273:         else if (className.equals("[C"))
 274:           {
 275:             try
 276:               {
 277:                 // CUPS only supports UTF-8 currently so we convert
 278:                 // We also assume that char[] is always utf-16 - correct ?
 279:                 String str = new String((char[]) doc.getPrintData());
 280:                 request.setData(str.getBytes("utf-16"));
 281:                 response = request.send();
 282:               }
 283:             catch (UnsupportedEncodingException e)
 284:               {
 285:                 notifyPrintJobListeners(new PrintJobEvent(this, PrintJobEvent.JOB_FAILED));
 286:                 throw new PrintFlavorException("Invalid charset of flavor", e, new DocFlavor[] {flavor});
 287:               }
 288:           }
 289:         else if (className.equals("java.io.Reader"))
 290:           {
 291:             try
 292:               {
 293:                 // FIXME Implement
 294:                 // Convert a Reader into a InputStream properly encoded
 295:                 response = request.send();
 296:                 throw new UnsupportedEncodingException("not supported yet");
 297:               }
 298:             catch (UnsupportedEncodingException e)
 299:               {
 300:                 notifyPrintJobListeners(new PrintJobEvent(this, PrintJobEvent.JOB_FAILED));
 301:                 throw new PrintFlavorException("Invalid charset of flavor", e, new DocFlavor[] {flavor});
 302:               }
 303:           }
 304:         else if (className.equals("java.lang.String"))
 305:           {
 306:             try
 307:               {
 308:                 // CUPS only supports UTF-8 currently so we convert
 309:                 // We also assume that String is always utf-16 - correct ?
 310:                 String str = (String) doc.getPrintData();
 311:                 request.setData(str.getBytes("utf-16"));
 312:                 response = request.send();
 313:               }
 314:             catch (UnsupportedEncodingException e)
 315:               {
 316:                 notifyPrintJobListeners(new PrintJobEvent(this, PrintJobEvent.JOB_FAILED));
 317:                 throw new PrintFlavorException("Invalid charset of flavor", e, new DocFlavor[] {flavor});
 318:               }
 319:           }
 320:         else if (className.equals("java.net.URL"))
 321:           {
 322:             URL url = (URL) doc.getPrintData();
 323:             InputStream stream = url.openStream();
 324:             request.setData(stream);
 325:             response = request.send();
 326:             stream.close();
 327:           }
 328:         else if (className.equals("java.awt.image.renderable.RenderableImage")
 329:                  || className.equals("java.awt.print.Printable")
 330:                  || className.equals("java.awt.print.Pageable"))
 331:           {
 332:             // For the future :-)
 333:             throw new PrintException("Not yet supported.");
 334:           }
 335:         else
 336:           {
 337:             // should not happen - however
 338:             notifyPrintJobListeners(new PrintJobEvent(this, PrintJobEvent.JOB_FAILED));
 339:             throw new PrintFlavorException("Invalid flavor", new DocFlavor[] {flavor});
 340:           }
 341: 
 342:         // at this point the data is transfered
 343:         notifyPrintJobListeners(new PrintJobEvent(
 344:           this, PrintJobEvent.DATA_TRANSFER_COMPLETE));
 345:       }
 346:     catch (IOException e)
 347:       {
 348:         throw new PrintException("IOException occured.", e);
 349:       }
 350: 
 351:     int status = response.getStatusCode();
 352:     if (! (status == IppStatusCode.SUCCESSFUL_OK
 353:          || status == IppStatusCode.SUCCESSFUL_OK_IGNORED_OR_SUBSTITUED_ATTRIBUTES
 354:          || status == IppStatusCode.SUCCESSFUL_OK_CONFLICTING_ATTRIBUTES) )
 355:       {
 356:         notifyPrintJobListeners(new PrintJobEvent(
 357:           this, PrintJobEvent.JOB_FAILED));
 358:         throw new PrintException("Printing failed - received statuscode " + Integer.toHexString(status));
 359: 
 360:         // TODO maybe specific status codes may require to throw a specific
 361:         // detailed attribute exception
 362:       }
 363:     else
 364:       {
 365:         // start print job progress monitoring thread
 366:         // FIXME Implement
 367: 
 368:         // for now we just notify as finished
 369:         notifyPrintJobListeners(
 370:           new PrintJobEvent(this, PrintJobEvent.JOB_COMPLETE));
 371:       }
 372: 
 373:     List jobAtts = response.getJobAttributes();
 374: 
 375:     // extract the uri and id of job for canceling and further monitoring
 376:     Map jobAttributes = (Map) jobAtts.get(0);
 377:     jobUri = (JobUri) ((HashSet)jobAttributes.get(JobUri.class)).toArray()[0];
 378:     jobId = (JobId) ((HashSet)jobAttributes.get(JobId.class)).toArray()[0];
 379:   }
 380: 
 381:   /**
 382:    * @see DocPrintJob#removePrintJobAttributeListener(PrintJobAttributeListener)
 383:    */
 384:   public void removePrintJobAttributeListener(PrintJobAttributeListener listener)
 385:   {
 386:     if (listener == null)
 387:       return;
 388: 
 389:     int index = attributesListener.indexOf(listener);
 390:     if (index != -1)
 391:       {
 392:         attributesListener.remove(index);
 393:         attributesListenerAttributes.remove(index);
 394:       }
 395:   }
 396: 
 397:   /**
 398:    * @see DocPrintJob#removePrintJobListener(PrintJobListener)
 399:    */
 400:   public void removePrintJobListener(PrintJobListener listener)
 401:   {
 402:     if (listener == null)
 403:       return;
 404: 
 405:     printJobListener.remove(listener);
 406:   }
 407: 
 408:   /**
 409:    * @see CancelablePrintJob#cancel()
 410:    */
 411:   public void cancel() throws PrintException
 412:   {
 413:     if (jobUri == null)
 414:       {
 415:         throw new PrintException("print job is not yet send");
 416:       }
 417: 
 418:     IppResponse response = null;
 419: 
 420:     try
 421:       {
 422:         IppRequest request = new IppRequest(jobUri.getURI(), username, password);
 423:         request.setOperationID( (short) OperationsSupported.CANCEL_JOB.getValue());
 424:         request.setOperationAttributeDefaults();
 425:         request.addOperationAttribute(jobUri);
 426:         request.addOperationAttribute(requestingUser);
 427:         response = request.send();
 428:       }
 429:     catch (IOException e)
 430:       {
 431:         throw new IppException("IOException occured during cancel request.", e);
 432:       }
 433: 
 434:     int status = response.getStatusCode();
 435:     if (! (status == IppStatusCode.SUCCESSFUL_OK
 436:          || status == IppStatusCode.SUCCESSFUL_OK_IGNORED_OR_SUBSTITUED_ATTRIBUTES
 437:          || status == IppStatusCode.SUCCESSFUL_OK_CONFLICTING_ATTRIBUTES) )
 438:       {
 439:         notifyPrintJobListeners(new PrintJobEvent(
 440:           this, PrintJobEvent.JOB_FAILED));
 441:         throw new PrintException("Canceling failed - received statuscode " + Integer.toHexString(status));
 442:       }
 443:     else
 444:       {
 445:         notifyPrintJobListeners(new PrintJobEvent(
 446:           this, PrintJobEvent.JOB_CANCELED));
 447:       }
 448:   }
 449: 
 450:   private void notifyPrintJobListeners(PrintJobEvent e)
 451:   {
 452:     Iterator it = printJobListener.iterator();
 453:     while (it.hasNext())
 454:       {
 455:         PrintJobListener l = (PrintJobListener) it.next();
 456:         if (e.getPrintEventType() == PrintJobEvent.DATA_TRANSFER_COMPLETE)
 457:           l.printDataTransferCompleted(e);
 458:         else if (e.getPrintEventType() == PrintJobEvent.JOB_CANCELED)
 459:           l.printJobCanceled(e);
 460:         else if (e.getPrintEventType() == PrintJobEvent.JOB_COMPLETE)
 461:           l.printJobCompleted(e);
 462:         else if (e.getPrintEventType() == PrintJobEvent.JOB_FAILED)
 463:           l.printJobFailed(e);
 464:         else if (e.getPrintEventType() == PrintJobEvent.NO_MORE_EVENTS)
 465:           l.printJobNoMoreEvents(e);
 466:         else
 467:           l.printJobRequiresAttention(e);
 468:       }
 469:   }
 470: 
 471: }