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 }
--- EOF ---