1 /* 2 * Copyright (c) 2000, 2014, 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 122 UnixPrintJob(PrintService service) { 123 this.service = service; 124 mDestination = service.getName(); 125 if (UnixPrintServiceLookup.isMac()) { 126 mDestination = ((IPPPrintService)service).getDest(); 127 } 128 mDestType = UnixPrintJob.DESTPRINTER; 129 } 130 131 public PrintService getPrintService() { 132 return service; 133 } 134 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 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 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 */ 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 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 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 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 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 ((service.getAttribute(PrinterIsAcceptingJobs.class)) == 319 PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) { 320 throw new PrintException("Printer is not accepting job."); 321 } 322 323 this.doc = doc; 324 /* check if the parameters are valid before doing much processing */ 325 DocFlavor flavor = doc.getDocFlavor(); 326 327 Object data; 328 329 try { 330 data = doc.getPrintData(); 331 } catch (IOException e) { 332 notifyEvent(PrintJobEvent.JOB_FAILED); 333 throw new PrintException("can't get print data: " + e.toString()); 334 } 335 336 if (data == null) { 337 throw new PrintException("Null print data."); 338 } 339 340 if (flavor == null || (!service.isDocFlavorSupported(flavor))) { 341 notifyEvent(PrintJobEvent.JOB_FAILED); 342 throw new PrintJobFlavorException("invalid flavor", flavor); 343 } 344 345 initializeAttributeSets(doc, attributes); 346 347 getAttributeValues(flavor); 348 349 // set up mOptions 350 if ((service instanceof IPPPrintService) && 351 CUPSPrinter.isCupsRunning()) { 352 353 IPPPrintService.debug_println(debugPrefix+ 354 "instanceof IPPPrintService"); 355 356 if (mediaName != null) { 357 CustomMediaSizeName customMedia = 358 ((IPPPrintService)service).findCustomMedia(mediaName); 359 if (customMedia != null) { 360 mOptions = " media="+ customMedia.getChoiceName(); 361 } 362 } 363 364 if (customTray != null && 365 customTray instanceof CustomMediaTray) { 366 String choice = customTray.getChoiceName(); 367 if (choice != null) { 368 mOptions += " media="+choice; 369 } 370 } 371 372 if (nUp != null) { 373 mOptions += " number-up="+nUp.getValue(); 374 } 375 376 if (orient != OrientationRequested.PORTRAIT && 377 (flavor != null) && 378 !flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE)) { 379 mOptions += " orientation-requested="+orient.getValue(); 380 } 381 382 if (sides != null) { 383 mOptions += " sides="+sides; 384 } 385 386 } 387 388 IPPPrintService.debug_println(debugPrefix+"mOptions "+mOptions); 389 String repClassName = flavor.getRepresentationClassName(); 390 String val = flavor.getParameter("charset"); 391 String encoding = "us-ascii"; 392 if (val != null && !val.equals("")) { 393 encoding = val; 394 } 395 396 if (flavor.equals(DocFlavor.INPUT_STREAM.GIF) || 397 flavor.equals(DocFlavor.INPUT_STREAM.JPEG) || 398 flavor.equals(DocFlavor.INPUT_STREAM.PNG) || 399 flavor.equals(DocFlavor.BYTE_ARRAY.GIF) || 400 flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) || 401 flavor.equals(DocFlavor.BYTE_ARRAY.PNG)) { 402 try { 403 instream = doc.getStreamForBytes(); 404 if (instream == null) { 405 notifyEvent(PrintJobEvent.JOB_FAILED); 406 throw new PrintException("No stream for data"); 407 } 408 if (!(service instanceof IPPPrintService && 409 ((IPPPrintService)service).isIPPSupportedImages( 410 flavor.getMimeType()))) { 411 printableJob(new ImagePrinter(instream)); 412 if (service instanceof IPPPrintService) { 413 ((IPPPrintService)service).wakeNotifier(); 414 } else { 415 ((UnixPrintService)service).wakeNotifier(); 416 } 417 return; 418 } 419 } catch (ClassCastException cce) { 420 notifyEvent(PrintJobEvent.JOB_FAILED); 421 throw new PrintException(cce); 422 } catch (IOException ioe) { 423 notifyEvent(PrintJobEvent.JOB_FAILED); 424 throw new PrintException(ioe); 425 } 426 } else if (flavor.equals(DocFlavor.URL.GIF) || 427 flavor.equals(DocFlavor.URL.JPEG) || 428 flavor.equals(DocFlavor.URL.PNG)) { 429 try { 430 URL url = (URL)data; 431 if ((service instanceof IPPPrintService) && 432 ((IPPPrintService)service).isIPPSupportedImages( 433 flavor.getMimeType())) { 434 instream = url.openStream(); 435 } else { 436 printableJob(new ImagePrinter(url)); 437 if (service instanceof IPPPrintService) { 438 ((IPPPrintService)service).wakeNotifier(); 439 } else { 440 ((UnixPrintService)service).wakeNotifier(); 441 } 442 return; 443 } 444 } catch (ClassCastException cce) { 445 notifyEvent(PrintJobEvent.JOB_FAILED); 446 throw new PrintException(cce); 447 } catch (IOException e) { 448 notifyEvent(PrintJobEvent.JOB_FAILED); 449 throw new PrintException(e.toString()); 450 } 451 } else if (flavor.equals(DocFlavor.CHAR_ARRAY.TEXT_PLAIN) || 452 flavor.equals(DocFlavor.READER.TEXT_PLAIN) || 453 flavor.equals(DocFlavor.STRING.TEXT_PLAIN)) { 454 try { 455 reader = doc.getReaderForText(); 456 if (reader == null) { 457 notifyEvent(PrintJobEvent.JOB_FAILED); 458 throw new PrintException("No reader for data"); 459 } 460 } catch (IOException ioe) { 461 notifyEvent(PrintJobEvent.JOB_FAILED); 462 throw new PrintException(ioe.toString()); 463 } 464 } else if (repClassName.equals("[B") || 465 repClassName.equals("java.io.InputStream")) { 466 try { 467 instream = doc.getStreamForBytes(); 468 if (instream == null) { 469 notifyEvent(PrintJobEvent.JOB_FAILED); 470 throw new PrintException("No stream for data"); 471 } 472 } catch (IOException ioe) { 473 notifyEvent(PrintJobEvent.JOB_FAILED); 474 throw new PrintException(ioe.toString()); 475 } 476 } else if (repClassName.equals("java.net.URL")) { 477 /* 478 * This extracts the data from the URL and passes it the content 479 * directly to the print service as a file. 480 * This is appropriate for the current implementation where lp or 481 * lpr is always used to spool the data. We expect to revise the 482 * implementation to provide more complete IPP support (ie not just 483 * CUPS) and at that time the job will be spooled via IPP 484 * and the URL 485 * itself should be sent to the IPP print service not the content. 486 */ 487 URL url = (URL)data; 488 try { 489 instream = url.openStream(); 490 } catch (IOException e) { 491 notifyEvent(PrintJobEvent.JOB_FAILED); 492 throw new PrintException(e.toString()); 493 } 494 } else if (repClassName.equals("java.awt.print.Pageable")) { 495 try { 496 pageableJob((Pageable)doc.getPrintData()); 497 if (service instanceof IPPPrintService) { 498 ((IPPPrintService)service).wakeNotifier(); 499 } else { 500 ((UnixPrintService)service).wakeNotifier(); 501 } 502 return; 503 } catch (ClassCastException cce) { 504 notifyEvent(PrintJobEvent.JOB_FAILED); 505 throw new PrintException(cce); 506 } catch (IOException ioe) { 507 notifyEvent(PrintJobEvent.JOB_FAILED); 508 throw new PrintException(ioe); 509 } 510 } else if (repClassName.equals("java.awt.print.Printable")) { 511 try { 512 printableJob((Printable)doc.getPrintData()); 513 if (service instanceof IPPPrintService) { 514 ((IPPPrintService)service).wakeNotifier(); 515 } else { 516 ((UnixPrintService)service).wakeNotifier(); 517 } 518 return; 519 } catch (ClassCastException cce) { 520 notifyEvent(PrintJobEvent.JOB_FAILED); 521 throw new PrintException(cce); 522 } catch (IOException ioe) { 523 notifyEvent(PrintJobEvent.JOB_FAILED); 524 throw new PrintException(ioe); 525 } 526 } else { 527 notifyEvent(PrintJobEvent.JOB_FAILED); 528 throw new PrintException("unrecognized class: "+repClassName); 529 } 530 531 // now spool the print data. 532 PrinterOpener po = new PrinterOpener(); 533 java.security.AccessController.doPrivileged(po); 534 if (po.pex != null) { 535 throw po.pex; 536 } 537 OutputStream output = po.result; 538 539 /* There are three cases: 540 * 1) Text data from a Reader, just pass through. 541 * 2) Text data from an input stream which we must read using the 542 * correct encoding 543 * 3) Raw byte data from an InputStream we don't interpret as text, 544 * just pass through: eg postscript. 545 */ 546 547 BufferedWriter bw = null; 548 if ((instream == null && reader != null)) { 549 BufferedReader br = new BufferedReader(reader); 550 OutputStreamWriter osw = new OutputStreamWriter(output); 551 bw = new BufferedWriter(osw); 552 char []buffer = new char[1024]; 553 int cread; 554 555 try { 556 while ((cread = br.read(buffer, 0, buffer.length)) >=0) { 557 bw.write(buffer, 0, cread); 558 } 559 br.close(); 560 bw.flush(); 561 bw.close(); 562 } catch (IOException e) { 563 notifyEvent(PrintJobEvent.JOB_FAILED); 564 throw new PrintException (e); 565 } 566 } else if (instream != null && 567 flavor.getMediaType().equalsIgnoreCase("text")) { 568 try { 569 570 InputStreamReader isr = new InputStreamReader(instream, 571 encoding); 572 BufferedReader br = new BufferedReader(isr); 573 OutputStreamWriter osw = new OutputStreamWriter(output); 574 bw = new BufferedWriter(osw); 575 char []buffer = new char[1024]; 576 int cread; 577 578 while ((cread = br.read(buffer, 0, buffer.length)) >=0) { 579 bw.write(buffer, 0, cread); 580 } 581 bw.flush(); 582 } catch (IOException e) { 583 notifyEvent(PrintJobEvent.JOB_FAILED); 584 throw new PrintException (e); 585 } finally { 586 try { 587 if (bw != null) { 588 bw.close(); 589 } 590 } catch (IOException e) { 591 } 592 } 593 } else if (instream != null) { 594 BufferedInputStream bin = new BufferedInputStream(instream); 595 BufferedOutputStream bout = new BufferedOutputStream(output); 596 byte[] buffer = new byte[1024]; 597 int bread = 0; 598 599 try { 600 while ((bread = bin.read(buffer)) >= 0) { 601 bout.write(buffer, 0, bread); 602 } 603 bin.close(); 604 bout.flush(); 605 bout.close(); 606 } catch (IOException e) { 607 notifyEvent(PrintJobEvent.JOB_FAILED); 608 throw new PrintException (e); 609 } 610 } 611 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 612 613 if (mDestType == UnixPrintJob.DESTPRINTER) { 614 PrinterSpooler spooler = new PrinterSpooler(); 615 java.security.AccessController.doPrivileged(spooler); 616 if (spooler.pex != null) { 617 throw spooler.pex; 618 } 619 } 620 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 621 if (service instanceof IPPPrintService) { 622 ((IPPPrintService)service).wakeNotifier(); 623 } else { 624 ((UnixPrintService)service).wakeNotifier(); 625 } 626 } 627 628 public void printableJob(Printable printable) throws PrintException { 629 try { 630 synchronized(this) { 631 if (job != null) { // shouldn't happen 632 throw new PrintException("already printing"); 633 } else { 634 job = new PSPrinterJob(); 635 } 636 } 637 job.setPrintService(getPrintService()); 638 job.setCopies(copies); 639 job.setJobName(jobName); 640 PageFormat pf = new PageFormat(); 641 if (mediaSize != null) { 642 Paper p = new Paper(); 643 p.setSize(mediaSize.getX(MediaSize.INCH)*72.0, 644 mediaSize.getY(MediaSize.INCH)*72.0); 645 p.setImageableArea(72.0, 72.0, p.getWidth()-144.0, 646 p.getHeight()-144.0); 647 pf.setPaper(p); 648 } 649 if (orient == OrientationRequested.REVERSE_LANDSCAPE) { 650 pf.setOrientation(PageFormat.REVERSE_LANDSCAPE); 651 } else if (orient == OrientationRequested.LANDSCAPE) { 652 pf.setOrientation(PageFormat.LANDSCAPE); 653 } 654 job.setPrintable(printable, pf); 655 job.print(reqAttrSet); 656 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 657 return; 658 } catch (PrinterException pe) { 659 notifyEvent(PrintJobEvent.JOB_FAILED); 660 throw new PrintException(pe); 661 } finally { 662 printReturned = true; 663 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 664 } 665 } 666 667 public void pageableJob(Pageable pageable) throws PrintException { 668 try { 669 synchronized(this) { 670 if (job != null) { // shouldn't happen 671 throw new PrintException("already printing"); 672 } else { 673 job = new PSPrinterJob(); 674 } 675 } 676 job.setPrintService(getPrintService()); 677 job.setCopies(copies); 678 job.setJobName(jobName); 679 job.setPageable(pageable); 680 job.print(reqAttrSet); 681 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 682 return; 683 } catch (PrinterException pe) { 684 notifyEvent(PrintJobEvent.JOB_FAILED); 685 throw new PrintException(pe); 686 } finally { 687 printReturned = true; 688 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 689 } 690 } 691 /* There's some inefficiency here as the job set is created even though 692 * it may never be requested. 693 */ 694 private synchronized void 695 initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet) { 696 697 reqAttrSet = new HashPrintRequestAttributeSet(); 698 jobAttrSet = new HashPrintJobAttributeSet(); 699 700 Attribute[] attrs; 701 if (reqSet != null) { 702 reqAttrSet.addAll(reqSet); 703 attrs = reqSet.toArray(); 704 for (int i=0; i<attrs.length; i++) { 705 if (attrs[i] instanceof PrintJobAttribute) { 706 jobAttrSet.add(attrs[i]); 707 } 708 } 709 } 710 711 DocAttributeSet docSet = doc.getAttributes(); 712 if (docSet != null) { 713 attrs = docSet.toArray(); 714 for (int i=0; i<attrs.length; i++) { 715 if (attrs[i] instanceof PrintRequestAttribute) { 716 reqAttrSet.add(attrs[i]); 717 } 718 if (attrs[i] instanceof PrintJobAttribute) { 719 jobAttrSet.add(attrs[i]); 720 } 721 } 722 } 723 724 /* add the user name to the job */ 725 String userName = ""; 726 try { 727 userName = System.getProperty("user.name"); 728 } catch (SecurityException se) { 729 } 730 731 if (userName == null || userName.equals("")) { 732 RequestingUserName ruName = 733 (RequestingUserName)reqSet.get(RequestingUserName.class); 734 if (ruName != null) { 735 jobAttrSet.add( 736 new JobOriginatingUserName(ruName.getValue(), 737 ruName.getLocale())); 738 } else { 739 jobAttrSet.add(new JobOriginatingUserName("", null)); 740 } 741 } else { 742 jobAttrSet.add(new JobOriginatingUserName(userName, null)); 743 } 744 745 /* if no job name supplied use doc name (if supplied), if none and 746 * its a URL use that, else finally anything .. */ 747 if (jobAttrSet.get(JobName.class) == null) { 748 JobName jobName; 749 if (docSet != null && docSet.get(DocumentName.class) != null) { 750 DocumentName docName = 751 (DocumentName)docSet.get(DocumentName.class); 752 jobName = new JobName(docName.getValue(), docName.getLocale()); 753 jobAttrSet.add(jobName); 754 } else { 755 String str = "JPS Job:" + doc; 756 try { 757 Object printData = doc.getPrintData(); 758 if (printData instanceof URL) { 759 str = ((URL)(doc.getPrintData())).toString(); 760 } 761 } catch (IOException e) { 762 } 763 jobName = new JobName(str, null); 764 jobAttrSet.add(jobName); 765 } 766 } 767 768 jobAttrSet = AttributeSetUtilities.unmodifiableView(jobAttrSet); 769 } 770 771 private void getAttributeValues(DocFlavor flavor) throws PrintException { 772 Attribute attr; 773 Class category; 774 775 if (reqAttrSet.get(Fidelity.class) == Fidelity.FIDELITY_TRUE) { 776 fidelity = true; 777 } else { 778 fidelity = false; 779 } 780 781 Attribute []attrs = reqAttrSet.toArray(); 782 for (int i=0; i<attrs.length; i++) { 783 attr = attrs[i]; 784 category = attr.getCategory(); 785 if (fidelity == true) { 786 if (!service.isAttributeCategorySupported(category)) { 787 notifyEvent(PrintJobEvent.JOB_FAILED); 788 throw new PrintJobAttributeException( 789 "unsupported category: " + category, category, null); 790 } else if 791 (!service.isAttributeValueSupported(attr, flavor, null)) { 792 notifyEvent(PrintJobEvent.JOB_FAILED); 793 throw new PrintJobAttributeException( 794 "unsupported attribute: " + attr, null, attr); 795 } 796 } 797 if (category == Destination.class) { 798 URI uri = ((Destination)attr).getURI(); 799 if (!"file".equals(uri.getScheme())) { 800 notifyEvent(PrintJobEvent.JOB_FAILED); 801 throw new PrintException("Not a file: URI"); 802 } else { 803 try { 804 mDestType = DESTFILE; 805 mDestination = (new File(uri)).getPath(); 806 } catch (Exception e) { 807 throw new PrintException(e); 808 } 809 // check write access 810 SecurityManager security = System.getSecurityManager(); 811 if (security != null) { 812 try { 813 security.checkWrite(mDestination); 814 } catch (SecurityException se) { 815 notifyEvent(PrintJobEvent.JOB_FAILED); 816 throw new PrintException(se); 817 } 818 } 819 } 820 } else if (category == JobSheets.class) { 821 if ((JobSheets)attr == JobSheets.NONE) { 822 mNoJobSheet = true; 823 } 824 } else if (category == JobName.class) { 825 jobName = ((JobName)attr).getValue(); 826 } else if (category == Copies.class) { 827 copies = ((Copies)attr).getValue(); 828 } else if (category == Media.class) { 829 if (attr instanceof MediaSizeName) { 830 mediaName = (MediaSizeName)attr; 831 IPPPrintService.debug_println(debugPrefix+ 832 "mediaName "+mediaName); 833 if (!service.isAttributeValueSupported(attr, null, null)) { 834 mediaSize = MediaSize.getMediaSizeForName(mediaName); 835 } 836 } else if (attr instanceof CustomMediaTray) { 837 customTray = (CustomMediaTray)attr; 838 } 839 } else if (category == OrientationRequested.class) { 840 orient = (OrientationRequested)attr; 841 } else if (category == NumberUp.class) { 842 nUp = (NumberUp)attr; 843 } else if (category == Sides.class) { 844 sides = (Sides)attr; 845 } 846 } 847 } 848 849 private String[] printExecCmd(String printer, String options, 850 boolean noJobSheet, 851 String banner, int copies, String spoolFile) { 852 int PRINTER = 0x1; 853 int OPTIONS = 0x2; 854 int BANNER = 0x4; 855 int COPIES = 0x8; 856 int NOSHEET = 0x10; 857 int pFlags = 0; 858 String execCmd[]; 859 int ncomps = 2; // minimum number of print args 860 int n = 0; 861 862 // conveniently "lp" is the default destination for both lp and lpr. 863 if (printer != null && !printer.equals("") && !printer.equals("lp")) { 864 pFlags |= PRINTER; 865 ncomps+=1; 866 } 867 if (options != null && !options.equals("")) { 868 pFlags |= OPTIONS; 869 ncomps+=1; 870 } 871 if (banner != null && !banner.equals("")) { 872 pFlags |= BANNER; 873 ncomps+=1; 874 } 875 if (copies > 1) { 876 pFlags |= COPIES; 877 ncomps+=1; 878 } 879 if (noJobSheet) { 880 pFlags |= NOSHEET; 881 ncomps+=1; 882 } 883 if (UnixPrintServiceLookup.osname.equals("SunOS")) { 884 ncomps+=1; // lp uses 1 more arg than lpr (make a copy) 885 execCmd = new String[ncomps]; 886 execCmd[n++] = "/usr/bin/lp"; 887 execCmd[n++] = "-c"; // make a copy of the spool file 888 if ((pFlags & PRINTER) != 0) { 889 execCmd[n++] = "-d" + printer; 890 } 891 if ((pFlags & BANNER) != 0) { 892 String quoteChar = "\""; 893 execCmd[n++] = "-t " + quoteChar+banner+quoteChar; 894 } 895 if ((pFlags & COPIES) != 0) { 896 execCmd[n++] = "-n " + copies; 897 } 898 if ((pFlags & NOSHEET) != 0) { 899 execCmd[n++] = "-o nobanner"; 900 } 901 if ((pFlags & OPTIONS) != 0) { 902 execCmd[n++] = "-o " + options; 903 } 904 } else { 905 execCmd = new String[ncomps]; 906 execCmd[n++] = "/usr/bin/lpr"; 907 if ((pFlags & PRINTER) != 0) { 908 execCmd[n++] = "-P" + printer; 909 } 910 if ((pFlags & BANNER) != 0) { 911 execCmd[n++] = "-J " + banner; 912 } 913 if ((pFlags & COPIES) != 0) { 914 execCmd[n++] = "-#" + copies; 915 } 916 if ((pFlags & NOSHEET) != 0) { 917 execCmd[n++] = "-h"; 918 } 919 if ((pFlags & OPTIONS) != 0) { 920 execCmd[n++] = "-o" + options; 921 } 922 } 923 execCmd[n++] = spoolFile; 924 if (IPPPrintService.debugPrint) { 925 System.out.println("UnixPrintJob>> execCmd"); 926 for (int i=0; i<execCmd.length; i++) { 927 System.out.print(" "+execCmd[i]); 928 } 929 System.out.println(); 930 } 931 return execCmd; 932 } 933 934 private static int DESTPRINTER = 1; 935 private static int DESTFILE = 2; 936 private int mDestType = DESTPRINTER; 937 938 private File spoolFile; 939 private String mDestination, mOptions=""; 940 private boolean mNoJobSheet = false; 941 942 // Inner class to run "privileged" to open the printer output stream. 943 944 private class PrinterOpener implements java.security.PrivilegedAction { 945 PrintException pex; 946 OutputStream result; 947 948 public Object run() { 949 try { 950 if (mDestType == UnixPrintJob.DESTFILE) { 951 spoolFile = new File(mDestination); 952 } else { 953 /* Write to a temporary file which will be spooled to 954 * the printer then deleted. In the case that the file 955 * is not removed for some reason, request that it is 956 * removed when the VM exits. 957 */ 958 spoolFile = Files.createTempFile("javaprint", "").toFile(); 959 spoolFile.deleteOnExit(); 960 } 961 result = new FileOutputStream(spoolFile); 962 return result; 963 } catch (IOException ex) { 964 // If there is an IOError we subvert it to a PrinterException. 965 notifyEvent(PrintJobEvent.JOB_FAILED); 966 pex = new PrintException(ex); 967 } 968 return null; 969 } 970 } 971 972 // Inner class to run "privileged" to invoke the system print command 973 974 @SuppressWarnings("finally") // Exception always thrown in finally block 975 private class PrinterSpooler implements java.security.PrivilegedAction { 976 PrintException pex; 977 978 private void handleProcessFailure(final Process failedProcess, 979 final String[] execCmd, final int result) throws IOException { 980 try (StringWriter sw = new StringWriter(); 981 PrintWriter pw = new PrintWriter(sw)) { 982 pw.append("error=").append(Integer.toString(result)); 983 pw.append(" running:"); 984 for (String arg: execCmd) { 985 pw.append(" '").append(arg).append("'"); 986 } 987 try (InputStream is = failedProcess.getErrorStream(); 988 InputStreamReader isr = new InputStreamReader(is); 989 BufferedReader br = new BufferedReader(isr)) { 990 while (br.ready()) { 991 pw.println(); 992 pw.append("\t\t").append(br.readLine()); 993 } 994 } finally { 995 pw.flush(); 996 throw new IOException(sw.toString()); 997 } 998 } 999 } 1000 1001 public Object run() { 1002 if (spoolFile == null || !spoolFile.exists()) { 1003 pex = new PrintException("No spool file"); 1004 notifyEvent(PrintJobEvent.JOB_FAILED); 1005 return null; 1006 } 1007 try { 1008 /** 1009 * Spool to the printer. 1010 */ 1011 String fileName = spoolFile.getAbsolutePath(); 1012 String execCmd[] = printExecCmd(mDestination, mOptions, 1013 mNoJobSheet, jobName, copies, fileName); 1014 1015 Process process = Runtime.getRuntime().exec(execCmd); 1016 process.waitFor(); 1017 final int result = process.exitValue(); 1018 if (0 != result) { 1019 handleProcessFailure(process, execCmd, result); 1020 } 1021 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 1022 } catch (IOException ex) { 1023 notifyEvent(PrintJobEvent.JOB_FAILED); 1024 // REMIND : 2d printing throws PrinterException 1025 pex = new PrintException(ex); 1026 } catch (InterruptedException ie) { 1027 notifyEvent(PrintJobEvent.JOB_FAILED); 1028 pex = new PrintException(ie); 1029 } finally { 1030 spoolFile.delete(); 1031 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 1032 } 1033 return null; 1034 } 1035 } 1036 1037 public void cancel() throws PrintException { 1038 synchronized (this) { 1039 if (!printing) { 1040 throw new PrintException("Job is not yet submitted."); 1041 } else if (job != null && !printReturned) { 1042 job.cancel(); 1043 notifyEvent(PrintJobEvent.JOB_CANCELED); 1044 return; 1045 } else { 1046 throw new PrintException("Job could not be cancelled."); 1047 } 1048 } 1049 } 1050 }