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