1 /* 2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.print; 27 28 import java.net.URI; 29 import java.net.URL; 30 import java.io.BufferedInputStream; 31 import java.io.BufferedOutputStream; 32 import java.io.BufferedReader; 33 import java.io.BufferedWriter; 34 import java.io.File; 35 import java.io.FileOutputStream; 36 import java.io.InputStream; 37 import java.io.InputStreamReader; 38 import java.io.OutputStream; 39 import java.io.OutputStreamWriter; 40 import java.io.IOException; 41 import java.io.PrintWriter; 42 import java.io.Reader; 43 import java.io.StringWriter; 44 import java.io.UnsupportedEncodingException; 45 import java.nio.file.Files; 46 import java.util.Vector; 47 48 import javax.print.CancelablePrintJob; 49 import javax.print.Doc; 50 import javax.print.DocFlavor; 51 import javax.print.DocPrintJob; 52 import javax.print.PrintService; 53 import javax.print.PrintException; 54 import javax.print.event.PrintJobEvent; 55 import javax.print.event.PrintJobListener; 56 import javax.print.event.PrintJobAttributeListener; 57 58 import javax.print.attribute.Attribute; 59 import javax.print.attribute.AttributeSet; 60 import javax.print.attribute.AttributeSetUtilities; 61 import javax.print.attribute.DocAttributeSet; 62 import javax.print.attribute.HashPrintJobAttributeSet; 63 import javax.print.attribute.HashPrintRequestAttributeSet; 64 import javax.print.attribute.PrintJobAttribute; 65 import javax.print.attribute.PrintJobAttributeSet; 66 import javax.print.attribute.PrintRequestAttribute; 67 import javax.print.attribute.PrintRequestAttributeSet; 68 import javax.print.attribute.PrintServiceAttributeSet; 69 import javax.print.attribute.standard.Copies; 70 import javax.print.attribute.standard.Destination; 71 import javax.print.attribute.standard.DocumentName; 72 import javax.print.attribute.standard.Fidelity; 73 import javax.print.attribute.standard.JobName; 74 import javax.print.attribute.standard.JobOriginatingUserName; 75 import javax.print.attribute.standard.JobSheets; 76 import javax.print.attribute.standard.Media; 77 import javax.print.attribute.standard.MediaSize; 78 import javax.print.attribute.standard.MediaSizeName; 79 import javax.print.attribute.standard.OrientationRequested; 80 import javax.print.attribute.standard.PrinterName; 81 import javax.print.attribute.standard.RequestingUserName; 82 import javax.print.attribute.standard.NumberUp; 83 import javax.print.attribute.standard.Sides; 84 import javax.print.attribute.standard.PrinterIsAcceptingJobs; 85 86 import java.awt.print.*; 87 88 89 90 public class UnixPrintJob implements CancelablePrintJob { 91 private static String debugPrefix = "UnixPrintJob>> "; 92 93 transient private Vector jobListeners; 94 transient private Vector attrListeners; 95 transient private Vector listenedAttributeSets; 96 97 private PrintService service; 98 private boolean fidelity; 99 private boolean printing = false; 100 private boolean printReturned = false; 101 private PrintRequestAttributeSet reqAttrSet = null; 102 private PrintJobAttributeSet jobAttrSet = null; 103 private PrinterJob job; 104 private Doc doc; 105 /* these variables used globally to store reference to the print 106 * data retrieved as a stream. On completion these are always closed 107 * if non-null. 108 */ 109 private InputStream instream = null; 110 private Reader reader = null; 111 112 /* default values overridden by those extracted from the attributes */ 113 private String jobName = "Java Printing"; 114 private int copies = 1; 115 private MediaSizeName mediaName = MediaSizeName.NA_LETTER; 116 private MediaSize mediaSize = MediaSize.NA.LETTER; 117 private CustomMediaTray customTray = null; 118 private OrientationRequested orient = OrientationRequested.PORTRAIT; 119 private NumberUp nUp = null; 120 private Sides sides = null; 121 UnixPrintJob(PrintService service)122 UnixPrintJob(PrintService service) { 123 this.service = service; 124 mDestination = service.getName(); 125 if (PrintServiceLookupProvider.isMac()) { 126 mDestination = ((IPPPrintService)service).getDest(); 127 } 128 mDestType = UnixPrintJob.DESTPRINTER; 129 } 130 getPrintService()131 public PrintService getPrintService() { 132 return service; 133 } 134 getAttributes()135 public PrintJobAttributeSet getAttributes() { 136 synchronized (this) { 137 if (jobAttrSet == null) { 138 /* just return an empty set until the job is submitted */ 139 PrintJobAttributeSet jobSet = new HashPrintJobAttributeSet(); 140 return AttributeSetUtilities.unmodifiableView(jobSet); 141 } else { 142 return jobAttrSet; 143 } 144 } 145 } 146 addPrintJobListener(PrintJobListener listener)147 public void addPrintJobListener(PrintJobListener listener) { 148 synchronized (this) { 149 if (listener == null) { 150 return; 151 } 152 if (jobListeners == null) { 153 jobListeners = new Vector(); 154 } 155 jobListeners.add(listener); 156 } 157 } 158 removePrintJobListener(PrintJobListener listener)159 public void removePrintJobListener(PrintJobListener listener) { 160 synchronized (this) { 161 if (listener == null || jobListeners == null ) { 162 return; 163 } 164 jobListeners.remove(listener); 165 if (jobListeners.isEmpty()) { 166 jobListeners = null; 167 } 168 } 169 } 170 171 172 /* Closes any stream already retrieved for the data. 173 * We want to avoid unnecessarily asking the Doc to create a stream only 174 * to get a reference in order to close it because the job failed. 175 * If the representation class is itself a "stream", this 176 * closes that stream too. 177 */ closeDataStreams()178 private void closeDataStreams() { 179 180 if (doc == null) { 181 return; 182 } 183 184 Object data = null; 185 186 try { 187 data = doc.getPrintData(); 188 } catch (IOException e) { 189 return; 190 } 191 192 if (instream != null) { 193 try { 194 instream.close(); 195 } catch (IOException e) { 196 } finally { 197 instream = null; 198 } 199 } 200 else if (reader != null) { 201 try { 202 reader.close(); 203 } catch (IOException e) { 204 } finally { 205 reader = null; 206 } 207 } 208 else if (data instanceof InputStream) { 209 try { 210 ((InputStream)data).close(); 211 } catch (IOException e) { 212 } 213 } 214 else if (data instanceof Reader) { 215 try { 216 ((Reader)data).close(); 217 } catch (IOException e) { 218 } 219 } 220 } 221 notifyEvent(int reason)222 private void notifyEvent(int reason) { 223 224 /* since this method should always get called, here's where 225 * we will perform the clean up of any data stream supplied. 226 */ 227 switch (reason) { 228 case PrintJobEvent.DATA_TRANSFER_COMPLETE: 229 case PrintJobEvent.JOB_CANCELED : 230 case PrintJobEvent.JOB_FAILED : 231 case PrintJobEvent.NO_MORE_EVENTS : 232 case PrintJobEvent.JOB_COMPLETE : 233 closeDataStreams(); 234 } 235 236 synchronized (this) { 237 if (jobListeners != null) { 238 PrintJobListener listener; 239 PrintJobEvent event = new PrintJobEvent(this, reason); 240 for (int i = 0; i < jobListeners.size(); i++) { 241 listener = (PrintJobListener)(jobListeners.elementAt(i)); 242 switch (reason) { 243 244 case PrintJobEvent.JOB_CANCELED : 245 listener.printJobCanceled(event); 246 break; 247 248 case PrintJobEvent.JOB_FAILED : 249 listener.printJobFailed(event); 250 break; 251 252 case PrintJobEvent.DATA_TRANSFER_COMPLETE : 253 listener.printDataTransferCompleted(event); 254 break; 255 256 case PrintJobEvent.NO_MORE_EVENTS : 257 listener.printJobNoMoreEvents(event); 258 break; 259 260 default: 261 break; 262 } 263 } 264 } 265 } 266 } 267 addPrintJobAttributeListener( PrintJobAttributeListener listener, PrintJobAttributeSet attributes)268 public void addPrintJobAttributeListener( 269 PrintJobAttributeListener listener, 270 PrintJobAttributeSet attributes) { 271 synchronized (this) { 272 if (listener == null) { 273 return; 274 } 275 if (attrListeners == null) { 276 attrListeners = new Vector(); 277 listenedAttributeSets = new Vector(); 278 } 279 attrListeners.add(listener); 280 if (attributes == null) { 281 attributes = new HashPrintJobAttributeSet(); 282 } 283 listenedAttributeSets.add(attributes); 284 } 285 } 286 removePrintJobAttributeListener( PrintJobAttributeListener listener)287 public void removePrintJobAttributeListener( 288 PrintJobAttributeListener listener) { 289 synchronized (this) { 290 if (listener == null || attrListeners == null ) { 291 return; 292 } 293 int index = attrListeners.indexOf(listener); 294 if (index == -1) { 295 return; 296 } else { 297 attrListeners.remove(index); 298 listenedAttributeSets.remove(index); 299 if (attrListeners.isEmpty()) { 300 attrListeners = null; 301 listenedAttributeSets = null; 302 } 303 } 304 } 305 } 306 print(Doc doc, PrintRequestAttributeSet attributes)307 public void print(Doc doc, PrintRequestAttributeSet attributes) 308 throws PrintException { 309 310 synchronized (this) { 311 if (printing) { 312 throw new PrintException("already printing"); 313 } else { 314 printing = true; 315 } 316 } 317 318 if ((PrinterIsAcceptingJobs)(service.getAttribute( 319 PrinterIsAcceptingJobs.class)) == 320 PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) { 321 throw new PrintException("Printer is not accepting job."); 322 } 323 324 this.doc = doc; 325 /* check if the parameters are valid before doing much processing */ 326 DocFlavor flavor = doc.getDocFlavor(); 327 328 Object data; 329 330 try { 331 data = doc.getPrintData(); 332 } catch (IOException e) { 333 notifyEvent(PrintJobEvent.JOB_FAILED); 334 throw new PrintException("can't get print data: " + e.toString()); 335 } 336 337 if (data == null) { 338 throw new PrintException("Null print data."); 339 } 340 341 if (flavor == null || (!service.isDocFlavorSupported(flavor))) { 342 notifyEvent(PrintJobEvent.JOB_FAILED); 343 throw new PrintJobFlavorException("invalid flavor", flavor); 344 } 345 346 initializeAttributeSets(doc, attributes); 347 348 getAttributeValues(flavor); 349 350 // set up mOptions 351 if ((service instanceof IPPPrintService) && 352 CUPSPrinter.isCupsRunning()) { 353 354 IPPPrintService.debug_println(debugPrefix+ 355 "instanceof IPPPrintService"); 356 357 if (mediaName != null) { 358 CustomMediaSizeName customMedia = 359 ((IPPPrintService)service).findCustomMedia(mediaName); 360 if (customMedia != null) { 361 mOptions = " media="+ customMedia.getChoiceName(); 362 } 363 } 364 365 if (customTray != null && 366 customTray instanceof CustomMediaTray) { 367 String choice = customTray.getChoiceName(); 368 if (choice != null) { 369 mOptions += " media="+choice; 370 } 371 } 372 373 if (nUp != null) { 374 mOptions += " number-up="+nUp.getValue(); 375 } 376 377 if (orient != OrientationRequested.PORTRAIT && 378 (flavor != null) && 379 !flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE)) { 380 mOptions += " orientation-requested="+orient.getValue(); 381 } 382 383 if (sides != null) { 384 mOptions += " sides="+sides; 385 } 386 387 } 388 389 IPPPrintService.debug_println(debugPrefix+"mOptions "+mOptions); 390 String repClassName = flavor.getRepresentationClassName(); 391 String val = flavor.getParameter("charset"); 392 String encoding = "us-ascii"; 393 if (val != null && !val.equals("")) { 394 encoding = val; 395 } 396 397 if (flavor.equals(DocFlavor.INPUT_STREAM.GIF) || 398 flavor.equals(DocFlavor.INPUT_STREAM.JPEG) || 399 flavor.equals(DocFlavor.INPUT_STREAM.PNG) || 400 flavor.equals(DocFlavor.BYTE_ARRAY.GIF) || 401 flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) || 402 flavor.equals(DocFlavor.BYTE_ARRAY.PNG)) { 403 try { 404 instream = doc.getStreamForBytes(); 405 if (instream == null) { 406 notifyEvent(PrintJobEvent.JOB_FAILED); 407 throw new PrintException("No stream for data"); 408 } 409 if (!(service instanceof IPPPrintService && 410 ((IPPPrintService)service).isIPPSupportedImages( 411 flavor.getMimeType()))) { 412 printableJob(new ImagePrinter(instream)); 413 ((UnixPrintService)service).wakeNotifier(); 414 return; 415 } 416 } catch (ClassCastException cce) { 417 notifyEvent(PrintJobEvent.JOB_FAILED); 418 throw new PrintException(cce); 419 } catch (IOException ioe) { 420 notifyEvent(PrintJobEvent.JOB_FAILED); 421 throw new PrintException(ioe); 422 } 423 } else if (flavor.equals(DocFlavor.URL.GIF) || 424 flavor.equals(DocFlavor.URL.JPEG) || 425 flavor.equals(DocFlavor.URL.PNG)) { 426 try { 427 URL url = (URL)data; 428 if ((service instanceof IPPPrintService) && 429 ((IPPPrintService)service).isIPPSupportedImages( 430 flavor.getMimeType())) { 431 instream = url.openStream(); 432 } else { 433 printableJob(new ImagePrinter(url)); 434 ((UnixPrintService)service).wakeNotifier(); 435 return; 436 } 437 } catch (ClassCastException cce) { 438 notifyEvent(PrintJobEvent.JOB_FAILED); 439 throw new PrintException(cce); 440 } catch (IOException e) { 441 notifyEvent(PrintJobEvent.JOB_FAILED); 442 throw new PrintException(e.toString()); 443 } 444 } else if (flavor.equals(DocFlavor.CHAR_ARRAY.TEXT_PLAIN) || 445 flavor.equals(DocFlavor.READER.TEXT_PLAIN) || 446 flavor.equals(DocFlavor.STRING.TEXT_PLAIN)) { 447 try { 448 reader = doc.getReaderForText(); 449 if (reader == null) { 450 notifyEvent(PrintJobEvent.JOB_FAILED); 451 throw new PrintException("No reader for data"); 452 } 453 } catch (IOException ioe) { 454 notifyEvent(PrintJobEvent.JOB_FAILED); 455 throw new PrintException(ioe.toString()); 456 } 457 } else if (repClassName.equals("[B") || 458 repClassName.equals("java.io.InputStream")) { 459 try { 460 instream = doc.getStreamForBytes(); 461 if (instream == null) { 462 notifyEvent(PrintJobEvent.JOB_FAILED); 463 throw new PrintException("No stream for data"); 464 } 465 } catch (IOException ioe) { 466 notifyEvent(PrintJobEvent.JOB_FAILED); 467 throw new PrintException(ioe.toString()); 468 } 469 } else if (repClassName.equals("java.net.URL")) { 470 /* 471 * This extracts the data from the URL and passes it the content 472 * directly to the print service as a file. 473 * This is appropriate for the current implementation where lp or 474 * lpr is always used to spool the data. We expect to revise the 475 * implementation to provide more complete IPP support (ie not just 476 * CUPS) and at that time the job will be spooled via IPP 477 * and the URL 478 * itself should be sent to the IPP print service not the content. 479 */ 480 URL url = (URL)data; 481 try { 482 instream = url.openStream(); 483 } catch (IOException e) { 484 notifyEvent(PrintJobEvent.JOB_FAILED); 485 throw new PrintException(e.toString()); 486 } 487 } else if (repClassName.equals("java.awt.print.Pageable")) { 488 try { 489 pageableJob((Pageable)doc.getPrintData()); 490 if (service instanceof IPPPrintService) { 491 ((IPPPrintService)service).wakeNotifier(); 492 } else { 493 ((UnixPrintService)service).wakeNotifier(); 494 } 495 return; 496 } catch (ClassCastException cce) { 497 notifyEvent(PrintJobEvent.JOB_FAILED); 498 throw new PrintException(cce); 499 } catch (IOException ioe) { 500 notifyEvent(PrintJobEvent.JOB_FAILED); 501 throw new PrintException(ioe); 502 } 503 } else if (repClassName.equals("java.awt.print.Printable")) { 504 try { 505 printableJob((Printable)doc.getPrintData()); 506 if (service instanceof IPPPrintService) { 507 ((IPPPrintService)service).wakeNotifier(); 508 } else { 509 ((UnixPrintService)service).wakeNotifier(); 510 } 511 return; 512 } catch (ClassCastException cce) { 513 notifyEvent(PrintJobEvent.JOB_FAILED); 514 throw new PrintException(cce); 515 } catch (IOException ioe) { 516 notifyEvent(PrintJobEvent.JOB_FAILED); 517 throw new PrintException(ioe); 518 } 519 } else { 520 notifyEvent(PrintJobEvent.JOB_FAILED); 521 throw new PrintException("unrecognized class: "+repClassName); 522 } 523 524 // now spool the print data. 525 PrinterOpener po = new PrinterOpener(); 526 java.security.AccessController.doPrivileged(po); 527 if (po.pex != null) { 528 throw po.pex; 529 } 530 OutputStream output = po.result; 531 532 /* There are three cases: 533 * 1) Text data from a Reader, just pass through. 534 * 2) Text data from an input stream which we must read using the 535 * correct encoding 536 * 3) Raw byte data from an InputStream we don't interpret as text, 537 * just pass through: eg postscript. 538 */ 539 540 BufferedWriter bw = null; 541 if ((instream == null && reader != null)) { 542 BufferedReader br = new BufferedReader(reader); 543 OutputStreamWriter osw = new OutputStreamWriter(output); 544 bw = new BufferedWriter(osw); 545 char []buffer = new char[1024]; 546 int cread; 547 548 try { 549 while ((cread = br.read(buffer, 0, buffer.length)) >=0) { 550 bw.write(buffer, 0, cread); 551 } 552 br.close(); 553 bw.flush(); 554 bw.close(); 555 } catch (IOException e) { 556 notifyEvent(PrintJobEvent.JOB_FAILED); 557 throw new PrintException (e); 558 } 559 } else if (instream != null && 560 flavor.getMediaType().equalsIgnoreCase("text")) { 561 try { 562 563 InputStreamReader isr = new InputStreamReader(instream, 564 encoding); 565 BufferedReader br = new BufferedReader(isr); 566 OutputStreamWriter osw = new OutputStreamWriter(output); 567 bw = new BufferedWriter(osw); 568 char []buffer = new char[1024]; 569 int cread; 570 571 while ((cread = br.read(buffer, 0, buffer.length)) >=0) { 572 bw.write(buffer, 0, cread); 573 } 574 bw.flush(); 575 } catch (IOException e) { 576 notifyEvent(PrintJobEvent.JOB_FAILED); 577 throw new PrintException (e); 578 } finally { 579 try { 580 if (bw != null) { 581 bw.close(); 582 } 583 } catch (IOException e) { 584 } 585 } 586 } else if (instream != null) { 587 BufferedInputStream bin = new BufferedInputStream(instream); 588 BufferedOutputStream bout = new BufferedOutputStream(output); 589 byte[] buffer = new byte[1024]; 590 int bread = 0; 591 592 try { 593 while ((bread = bin.read(buffer)) >= 0) { 594 bout.write(buffer, 0, bread); 595 } 596 bin.close(); 597 bout.flush(); 598 bout.close(); 599 } catch (IOException e) { 600 notifyEvent(PrintJobEvent.JOB_FAILED); 601 throw new PrintException (e); 602 } 603 } 604 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 605 606 if (mDestType == UnixPrintJob.DESTPRINTER) { 607 PrinterSpooler spooler = new PrinterSpooler(); 608 java.security.AccessController.doPrivileged(spooler); 609 if (spooler.pex != null) { 610 throw spooler.pex; 611 } 612 } 613 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 614 if (service instanceof IPPPrintService) { 615 ((IPPPrintService)service).wakeNotifier(); 616 } else { 617 ((UnixPrintService)service).wakeNotifier(); 618 } 619 } 620 printableJob(Printable printable)621 public void printableJob(Printable printable) throws PrintException { 622 try { 623 synchronized(this) { 624 if (job != null) { // shouldn't happen 625 throw new PrintException("already printing"); 626 } else { 627 job = new PSPrinterJob(); 628 } 629 } 630 job.setPrintService(getPrintService()); 631 job.setCopies(copies); 632 job.setJobName(jobName); 633 PageFormat pf = new PageFormat(); 634 if (mediaSize != null) { 635 Paper p = new Paper(); 636 p.setSize(mediaSize.getX(MediaSize.INCH)*72.0, 637 mediaSize.getY(MediaSize.INCH)*72.0); 638 p.setImageableArea(72.0, 72.0, p.getWidth()-144.0, 639 p.getHeight()-144.0); 640 pf.setPaper(p); 641 } 642 if (orient == OrientationRequested.REVERSE_LANDSCAPE) { 643 pf.setOrientation(PageFormat.REVERSE_LANDSCAPE); 644 } else if (orient == OrientationRequested.LANDSCAPE) { 645 pf.setOrientation(PageFormat.LANDSCAPE); 646 } 647 job.setPrintable(printable, pf); 648 job.print(reqAttrSet); 649 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 650 return; 651 } catch (PrinterException pe) { 652 notifyEvent(PrintJobEvent.JOB_FAILED); 653 throw new PrintException(pe); 654 } finally { 655 printReturned = true; 656 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 657 } 658 } 659 pageableJob(Pageable pageable)660 public void pageableJob(Pageable pageable) throws PrintException { 661 try { 662 synchronized(this) { 663 if (job != null) { // shouldn't happen 664 throw new PrintException("already printing"); 665 } else { 666 job = new PSPrinterJob(); 667 } 668 } 669 job.setPrintService(getPrintService()); 670 job.setCopies(copies); 671 job.setJobName(jobName); 672 job.setPageable(pageable); 673 job.print(reqAttrSet); 674 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 675 return; 676 } catch (PrinterException pe) { 677 notifyEvent(PrintJobEvent.JOB_FAILED); 678 throw new PrintException(pe); 679 } finally { 680 printReturned = true; 681 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 682 } 683 } 684 /* There's some inefficiency here as the job set is created even though 685 * it may never be requested. 686 */ 687 private synchronized void initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet)688 initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet) { 689 690 reqAttrSet = new HashPrintRequestAttributeSet(); 691 jobAttrSet = new HashPrintJobAttributeSet(); 692 693 Attribute[] attrs; 694 if (reqSet != null) { 695 reqAttrSet.addAll(reqSet); 696 attrs = reqSet.toArray(); 697 for (int i=0; i<attrs.length; i++) { 698 if (attrs[i] instanceof PrintJobAttribute) { 699 jobAttrSet.add(attrs[i]); 700 } 701 } 702 } 703 704 DocAttributeSet docSet = doc.getAttributes(); 705 if (docSet != null) { 706 attrs = docSet.toArray(); 707 for (int i=0; i<attrs.length; i++) { 708 if (attrs[i] instanceof PrintRequestAttribute) { 709 reqAttrSet.add(attrs[i]); 710 } 711 if (attrs[i] instanceof PrintJobAttribute) { 712 jobAttrSet.add(attrs[i]); 713 } 714 } 715 } 716 717 /* add the user name to the job */ 718 String userName = ""; 719 try { 720 userName = System.getProperty("user.name"); 721 } catch (SecurityException se) { 722 } 723 724 if (userName == null || userName.equals("")) { 725 RequestingUserName ruName = 726 (RequestingUserName)reqSet.get(RequestingUserName.class); 727 if (ruName != null) { 728 jobAttrSet.add( 729 new JobOriginatingUserName(ruName.getValue(), 730 ruName.getLocale())); 731 } else { 732 jobAttrSet.add(new JobOriginatingUserName("", null)); 733 } 734 } else { 735 jobAttrSet.add(new JobOriginatingUserName(userName, null)); 736 } 737 738 /* if no job name supplied use doc name (if supplied), if none and 739 * its a URL use that, else finally anything .. */ 740 if (jobAttrSet.get(JobName.class) == null) { 741 JobName jobName; 742 if (docSet != null && docSet.get(DocumentName.class) != null) { 743 DocumentName docName = 744 (DocumentName)docSet.get(DocumentName.class); 745 jobName = new JobName(docName.getValue(), docName.getLocale()); 746 jobAttrSet.add(jobName); 747 } else { 748 String str = "JPS Job:" + doc; 749 try { 750 Object printData = doc.getPrintData(); 751 if (printData instanceof URL) { 752 str = ((URL)(doc.getPrintData())).toString(); 753 } 754 } catch (IOException e) { 755 } 756 jobName = new JobName(str, null); 757 jobAttrSet.add(jobName); 758 } 759 } 760 761 jobAttrSet = AttributeSetUtilities.unmodifiableView(jobAttrSet); 762 } 763 getAttributeValues(DocFlavor flavor)764 private void getAttributeValues(DocFlavor flavor) throws PrintException { 765 Attribute attr; 766 Class category; 767 768 if (reqAttrSet.get(Fidelity.class) == Fidelity.FIDELITY_TRUE) { 769 fidelity = true; 770 } else { 771 fidelity = false; 772 } 773 774 Attribute []attrs = reqAttrSet.toArray(); 775 for (int i=0; i<attrs.length; i++) { 776 attr = attrs[i]; 777 category = attr.getCategory(); 778 if (fidelity == true) { 779 if (!service.isAttributeCategorySupported(category)) { 780 notifyEvent(PrintJobEvent.JOB_FAILED); 781 throw new PrintJobAttributeException( 782 "unsupported category: " + category, category, null); 783 } else if 784 (!service.isAttributeValueSupported(attr, flavor, null)) { 785 notifyEvent(PrintJobEvent.JOB_FAILED); 786 throw new PrintJobAttributeException( 787 "unsupported attribute: " + attr, null, attr); 788 } 789 } 790 if (category == Destination.class) { 791 URI uri = ((Destination)attr).getURI(); 792 if (!"file".equals(uri.getScheme())) { 793 notifyEvent(PrintJobEvent.JOB_FAILED); 794 throw new PrintException("Not a file: URI"); 795 } else { 796 try { 797 mDestType = DESTFILE; 798 mDestination = (new File(uri)).getPath(); 799 } catch (Exception e) { 800 throw new PrintException(e); 801 } 802 // check write access 803 SecurityManager security = System.getSecurityManager(); 804 if (security != null) { 805 try { 806 security.checkWrite(mDestination); 807 } catch (SecurityException se) { 808 notifyEvent(PrintJobEvent.JOB_FAILED); 809 throw new PrintException(se); 810 } 811 } 812 } 813 } else if (category == JobSheets.class) { 814 if ((JobSheets)attr == JobSheets.NONE) { 815 mNoJobSheet = true; 816 } 817 } else if (category == JobName.class) { 818 jobName = ((JobName)attr).getValue(); 819 } else if (category == Copies.class) { 820 copies = ((Copies)attr).getValue(); 821 } else if (category == Media.class) { 822 if (attr instanceof MediaSizeName) { 823 mediaName = (MediaSizeName)attr; 824 IPPPrintService.debug_println(debugPrefix+ 825 "mediaName "+mediaName); 826 if (!service.isAttributeValueSupported(attr, null, null)) { 827 mediaSize = MediaSize.getMediaSizeForName(mediaName); 828 } 829 } else if (attr instanceof CustomMediaTray) { 830 customTray = (CustomMediaTray)attr; 831 } 832 } else if (category == OrientationRequested.class) { 833 orient = (OrientationRequested)attr; 834 } else if (category == NumberUp.class) { 835 nUp = (NumberUp)attr; 836 } else if (category == Sides.class) { 837 sides = (Sides)attr; 838 } 839 } 840 } 841 printExecCmd(String printer, String options, boolean noJobSheet, String banner, int copies, String spoolFile)842 private String[] printExecCmd(String printer, String options, 843 boolean noJobSheet, 844 String banner, int copies, String spoolFile) { 845 int PRINTER = 0x1; 846 int OPTIONS = 0x2; 847 int BANNER = 0x4; 848 int COPIES = 0x8; 849 int NOSHEET = 0x10; 850 int pFlags = 0; 851 String execCmd[]; 852 int ncomps = 2; // minimum number of print args 853 int n = 0; 854 855 // conveniently "lp" is the default destination for both lp and lpr. 856 if (printer != null && !printer.equals("") && !printer.equals("lp")) { 857 pFlags |= PRINTER; 858 ncomps+=1; 859 } 860 if (options != null && !options.equals("")) { 861 pFlags |= OPTIONS; 862 ncomps+=1; 863 } 864 if (banner != null && !banner.equals("")) { 865 pFlags |= BANNER; 866 ncomps+=1; 867 } 868 if (copies > 1) { 869 pFlags |= COPIES; 870 ncomps+=1; 871 } 872 if (noJobSheet) { 873 pFlags |= NOSHEET; 874 ncomps+=1; 875 } 876 if (PrintServiceLookupProvider.osname.equals("SunOS")) { 877 ncomps+=1; // lp uses 1 more arg than lpr (make a copy) 878 execCmd = new String[ncomps]; 879 execCmd[n++] = "/usr/bin/lp"; 880 execCmd[n++] = "-c"; // make a copy of the spool file 881 if ((pFlags & PRINTER) != 0) { 882 execCmd[n++] = "-d" + printer; 883 } 884 if ((pFlags & BANNER) != 0) { 885 String quoteChar = "\""; 886 execCmd[n++] = "-t " + quoteChar+banner+quoteChar; 887 } 888 if ((pFlags & COPIES) != 0) { 889 execCmd[n++] = "-n " + copies; 890 } 891 if ((pFlags & NOSHEET) != 0) { 892 execCmd[n++] = "-o nobanner"; 893 } 894 if ((pFlags & OPTIONS) != 0) { 895 execCmd[n++] = "-o " + options; 896 } 897 } else { 898 execCmd = new String[ncomps]; 899 execCmd[n++] = "/usr/bin/lpr"; 900 if ((pFlags & PRINTER) != 0) { 901 execCmd[n++] = "-P" + printer; 902 } 903 if ((pFlags & BANNER) != 0) { 904 execCmd[n++] = "-J " + banner; 905 } 906 if ((pFlags & COPIES) != 0) { 907 execCmd[n++] = "-#" + copies; 908 } 909 if ((pFlags & NOSHEET) != 0) { 910 execCmd[n++] = "-h"; 911 } 912 if ((pFlags & OPTIONS) != 0) { 913 execCmd[n++] = "-o" + options; 914 } 915 } 916 execCmd[n++] = spoolFile; 917 if (IPPPrintService.debugPrint) { 918 System.out.println("UnixPrintJob>> execCmd"); 919 for (int i=0; i<execCmd.length; i++) { 920 System.out.print(" "+execCmd[i]); 921 } 922 System.out.println(); 923 } 924 return execCmd; 925 } 926 927 private static int DESTPRINTER = 1; 928 private static int DESTFILE = 2; 929 private int mDestType = DESTPRINTER; 930 931 private File spoolFile; 932 private String mDestination, mOptions=""; 933 private boolean mNoJobSheet = false; 934 935 // Inner class to run "privileged" to open the printer output stream. 936 937 private class PrinterOpener implements java.security.PrivilegedAction { 938 PrintException pex; 939 OutputStream result; 940 run()941 public Object run() { 942 try { 943 if (mDestType == UnixPrintJob.DESTFILE) { 944 spoolFile = new File(mDestination); 945 } else { 946 /* Write to a temporary file which will be spooled to 947 * the printer then deleted. In the case that the file 948 * is not removed for some reason, request that it is 949 * removed when the VM exits. 950 */ 951 spoolFile = Files.createTempFile("javaprint", "").toFile(); 952 spoolFile.deleteOnExit(); 953 } 954 result = new FileOutputStream(spoolFile); 955 return result; 956 } catch (IOException ex) { 957 // If there is an IOError we subvert it to a PrinterException. 958 notifyEvent(PrintJobEvent.JOB_FAILED); 959 pex = new PrintException(ex); 960 } 961 return null; 962 } 963 } 964 965 // Inner class to run "privileged" to invoke the system print command 966 967 private class PrinterSpooler implements java.security.PrivilegedAction { 968 PrintException pex; 969 handleProcessFailure(final Process failedProcess, final String[] execCmd, final int result)970 private void handleProcessFailure(final Process failedProcess, 971 final String[] execCmd, final int result) throws IOException { 972 try (StringWriter sw = new StringWriter(); 973 PrintWriter pw = new PrintWriter(sw)) { 974 pw.append("error=").append(Integer.toString(result)); 975 pw.append(" running:"); 976 for (String arg: execCmd) { 977 pw.append(" '").append(arg).append("'"); 978 } 979 try (InputStream is = failedProcess.getErrorStream(); 980 InputStreamReader isr = new InputStreamReader(is); 981 BufferedReader br = new BufferedReader(isr)) { 982 while (br.ready()) { 983 pw.println(); 984 pw.append("\t\t").append(br.readLine()); 985 } 986 } finally { 987 pw.flush(); 988 throw new IOException(sw.toString()); 989 } 990 } 991 } 992 run()993 public Object run() { 994 if (spoolFile == null || !spoolFile.exists()) { 995 pex = new PrintException("No spool file"); 996 notifyEvent(PrintJobEvent.JOB_FAILED); 997 return null; 998 } 999 try { 1000 /** 1001 * Spool to the printer. 1002 */ 1003 String fileName = spoolFile.getAbsolutePath(); 1004 String execCmd[] = printExecCmd(mDestination, mOptions, 1005 mNoJobSheet, jobName, copies, fileName); 1006 1007 Process process = Runtime.getRuntime().exec(execCmd); 1008 process.waitFor(); 1009 final int result = process.exitValue(); 1010 if (0 != result) { 1011 handleProcessFailure(process, execCmd, result); 1012 } 1013 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 1014 } catch (IOException ex) { 1015 notifyEvent(PrintJobEvent.JOB_FAILED); 1016 // REMIND : 2d printing throws PrinterException 1017 pex = new PrintException(ex); 1018 } catch (InterruptedException ie) { 1019 notifyEvent(PrintJobEvent.JOB_FAILED); 1020 pex = new PrintException(ie); 1021 } finally { 1022 spoolFile.delete(); 1023 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 1024 } 1025 return null; 1026 } 1027 } 1028 cancel()1029 public void cancel() throws PrintException { 1030 synchronized (this) { 1031 if (!printing) { 1032 throw new PrintException("Job is not yet submitted."); 1033 } else if (job != null && !printReturned) { 1034 job.cancel(); 1035 notifyEvent(PrintJobEvent.JOB_CANCELED); 1036 return; 1037 } else { 1038 throw new PrintException("Job could not be cancelled."); 1039 } 1040 } 1041 } 1042 } 1043