1 /*
2 * Copyright 2000-2002 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27 package javax.print;
28
29 import java.util.ArrayList;
30 import java.util.Iterator;
31 import java.util.List;
32 import javax.print.attribute.AttributeSet;
33
34 import sun.awt.AppContext;
35 import java.util.ServiceLoader;
36 import java.util.ServiceConfigurationError;
37
38 /** Implementations of this class provide lookup services for
39 * print services (typically equivalent to printers) of a particular type.
40 * <p>
41 * Multiple implementations may be installed concurrently.
42 * All implementations must be able to describe the located printers
43 * as instances of a PrintService.
44 * Typically implementations of this service class are located
45 * automatically in JAR files (see the SPI JAR file specification).
46 * These classes must be instantiable using a default constructor.
47 * Alternatively applications may explicitly register instances
48 * at runtime.
49 * <p>
50 * Applications use only the static methods of this abstract class.
51 * The instance methods are implemented by a service provider in a subclass
52 * and the unification of the results from all installed lookup classes
53 * are reported by the static methods of this class when called by
54 * the application.
55 * <p>
56 * A PrintServiceLookup implementor is recommended to check for the
57 * SecurityManager.checkPrintJobAccess() to deny access to untrusted code.
58 * Following this recommended policy means that untrusted code may not
59 * be able to locate any print services. Downloaded applets are the most
60 * common example of untrusted code.
61 * <p>
62 * This check is made on a per lookup service basis to allow flexibility in
63 * the policy to reflect the needs of different lookup services.
64 * <p>
65 * Services which are registered by registerService(PrintService)
66 * will not be included in lookup results if a security manager is
67 * installed and its checkPrintJobAccess() method denies access.
68 */
69
70 public abstract class PrintServiceLookup {
71
72 static class Services {
73 private ArrayList listOfLookupServices = null;
74 private ArrayList registeredServices = null;
75 }
76
77 private static Services getServicesForContext() {
78 Services services =
79 (Services)AppContext.getAppContext().get(Services.class);
80 if (services == null) {
81 services = new Services();
82 AppContext.getAppContext().put(Services.class, services);
83 }
84 return services;
85 }
86
87 private static ArrayList getListOfLookupServices() {
88 return getServicesForContext().listOfLookupServices;
89 }
90
91 private static ArrayList initListOfLookupServices() {
92 ArrayList listOfLookupServices = new ArrayList();
93 getServicesForContext().listOfLookupServices = listOfLookupServices;
94 return listOfLookupServices;
95 }
96
97
98 private static ArrayList getRegisteredServices() {
99 return getServicesForContext().registeredServices;
100 }
101
102 private static ArrayList initRegisteredServices() {
103 ArrayList registeredServices = new ArrayList();
104 getServicesForContext().registeredServices = registeredServices;
105 return registeredServices;
106 }
107
108 /**
109 * Locates print services capable of printing the specified
110 * {@link DocFlavor}.
111 *
112 * @param flavor the flavor to print. If null, this constraint is not
113 * used.
114 * @param attributes attributes that the print service must support.
115 * If null this constraint is not used.
116 *
117 * @return array of matching <code>PrintService</code> objects
118 * representing print services that support the specified flavor
119 * attributes. If no services match, the array is zero-length.
120 */
121 public static final PrintService[]
122 lookupPrintServices(DocFlavor flavor,
123 AttributeSet attributes) {
124 ArrayList list = getServices(flavor, attributes);
125 return (PrintService[])(list.toArray(new PrintService[list.size()]));
126 }
127
128
129 /**
130 * Locates MultiDoc print Services capable of printing MultiDocs
131 * containing all the specified doc flavors.
132 * <P> This method is useful to help locate a service that can print
133 * a <code>MultiDoc</code> in which the elements may be different
134 * flavors. An application could perform this itself by multiple lookups
135 * on each <code>DocFlavor</code> in turn and collating the results,
136 * but the lookup service may be able to do this more efficiently.
137 *
138 * @param flavors the flavors to print. If null or empty this
139 * constraint is not used.
140 * Otherwise return only multidoc print services that can print all
141 * specified doc flavors.
142 * @param attributes attributes that the print service must
143 * support. If null this constraint is not used.
144 *
145 * @return array of matching {@link MultiDocPrintService} objects.
146 * If no services match, the array is zero-length.
147 *
148 */
149 public static final MultiDocPrintService[]
150 lookupMultiDocPrintServices(DocFlavor[] flavors,
151 AttributeSet attributes) {
152 ArrayList list = getMultiDocServices(flavors, attributes);
153 return (MultiDocPrintService[])
154 list.toArray(new MultiDocPrintService[list.size()]);
155 }
156
157
158 /**
159 * Locates the default print service for this environment.
160 * This may return null.
161 * If multiple lookup services each specify a default, the
162 * chosen service is not precisely defined, but a
163 * platform native service, rather than an installed service,
164 * is usually returned as the default. If there is no clearly
165 * identifiable
166 * platform native default print service, the default is the first
167 * to be located in an implementation-dependent manner.
168 * <p>
169 * This may include making use of any preferences API that is available
170 * as part of the Java or native platform.
171 * This algorithm may be overridden by a user setting the property
172 * javax.print.defaultPrinter.
173 * A service specified must be discovered to be valid and currently
174 * available to be returned as the default.
175 *
176 * @return the default PrintService.
177 */
178
179 public static final PrintService lookupDefaultPrintService() {
180
181 Iterator psIterator = getAllLookupServices().iterator();
182 while (psIterator.hasNext()) {
183 try {
184 PrintServiceLookup lus = (PrintServiceLookup)psIterator.next();
185 PrintService service = lus.getDefaultPrintService();
186 if (service != null) {
187 return service;
188 }
189 } catch (Exception e) {
190 }
191 }
192 return null;
193 }
194
195
196 /**
197 * Allows an application to explicitly register a class that
198 * implements lookup services. The registration will not persist
199 * across VM invocations.
200 * This is useful if an application needs to make a new service
201 * available that is not part of the installation.
202 * If the lookup service is already registered, or cannot be registered,
203 * the method returns false.
204 * <p>
205 *
206 * @param sp an implementation of a lookup service.
207 * @return <code>true</code> if the new lookup service is newly
208 * registered; <code>false</code> otherwise.
209 */
210 public static boolean registerServiceProvider(PrintServiceLookup sp) {
211 synchronized (PrintServiceLookup.class) {
212 Iterator psIterator = getAllLookupServices().iterator();
213 while (psIterator.hasNext()) {
214 try {
215 Object lus = psIterator.next();
216 if (lus.getClass() == sp.getClass()) {
217 return false;
218 }
219 } catch (Exception e) {
220 }
221 }
222 getListOfLookupServices().add(sp);
223 return true;
224 }
225
226 }
227
228
229 /**
230 * Allows an application to directly register an instance of a
231 * class which implements a print service.
232 * The lookup operations for this service will be
233 * performed by the PrintServiceLookup class using the attribute
234 * values and classes reported by the service.
235 * This may be less efficient than a lookup
236 * service tuned for that service.
237 * Therefore registering a <code>PrintServiceLookup</code> instance
238 * instead is recommended.
239 * The method returns true if this service is not previously
240 * registered and is now successfully registered.
241 * This method should not be called with StreamPrintService instances.
242 * They will always fail to register and the method will return false.
243 * @param service an implementation of a print service.
244 * @return <code>true</code> if the service is newly
245 * registered; <code>false</code> otherwise.
246 */
247
248 public static boolean registerService(PrintService service) {
249 synchronized (PrintServiceLookup.class) {
250 if (service instanceof StreamPrintService) {
251 return false;
252 }
253 ArrayList registeredServices = getRegisteredServices();
254 if (registeredServices == null) {
255 registeredServices = initRegisteredServices();
256 }
257 else {
258 if (registeredServices.contains(service)) {
259 return false;
260 }
261 }
262 registeredServices.add(service);
263 return true;
264 }
265 }
266
267
268 /**
269 * Locates services that can be positively confirmed to support
270 * the combination of attributes and DocFlavors specified.
271 * This method is not called directly by applications.
272 * <p>
273 * Implemented by a service provider, used by the static methods
274 * of this class.
275 * <p>
276 * The results should be the same as obtaining all the PrintServices
277 * and querying each one individually on its support for the
278 * specified attributes and flavors, but the process can be more
279 * efficient by taking advantage of the capabilities of lookup services
280 * for the print services.
281 *
282 * @param flavor of document required. If null it is ignored.
283 * @param attributes required to be supported. If null this
284 * constraint is not used.
285 * @return array of matching PrintServices. If no services match, the
286 * array is zero-length.
287 */
288 public abstract PrintService[] getPrintServices(DocFlavor flavor,
289 AttributeSet attributes);
290
291 /**
292 * Not called directly by applications.
293 * Implemented by a service provider, used by the static methods
294 * of this class.
295 * @return array of all PrintServices known to this lookup service
296 * class. If none are found, the array is zero-length.
297 */
298 public abstract PrintService[] getPrintServices() ;
299
300
301 /**
302 * Not called directly by applications.
303 * <p>
304 * Implemented by a service provider, used by the static methods
305 * of this class.
306 * <p>
307 * Locates MultiDoc print services which can be positively confirmed
308 * to support the combination of attributes and DocFlavors specified.
309 * <p>
310 *
311 * @param flavors of documents required. If null or empty it is ignored.
312 * @param attributes required to be supported. If null this
313 * constraint is not used.
314 * @return array of matching PrintServices. If no services match, the
315 * array is zero-length.
316 */
317 public abstract MultiDocPrintService[]
318 getMultiDocPrintServices(DocFlavor[] flavors,
319 AttributeSet attributes);
320
321 /**
322 * Not called directly by applications.
323 * Implemented by a service provider, and called by the print lookup
324 * service
325 * @return the default PrintService for this lookup service.
326 * If there is no default, returns null.
327 */
328 public abstract PrintService getDefaultPrintService();
329
330 private static ArrayList getAllLookupServices() {
331 synchronized (PrintServiceLookup.class) {
332 ArrayList listOfLookupServices = getListOfLookupServices();
333 if (listOfLookupServices != null) {
334 return listOfLookupServices;
335 } else {
336 listOfLookupServices = initListOfLookupServices();
337 }
338 try {
339 java.security.AccessController.doPrivileged(
340 new java.security.PrivilegedExceptionAction() {
341 public Object run() {
342 Iterator<PrintServiceLookup> iterator =
343 ServiceLoader.load(PrintServiceLookup.class).
344 iterator();
345 ArrayList los = getListOfLookupServices();
346 while (iterator.hasNext()) {
347 try {
348 los.add(iterator.next());
349 } catch (ServiceConfigurationError err) {
350 /* In the applet case, we continue */
351 if (System.getSecurityManager() != null) {
352 err.printStackTrace();
353 } else {
354 throw err;
355 }
356 }
357 }
358 return null;
359 }
360 });
361 } catch (java.security.PrivilegedActionException e) {
362 }
363
364 return listOfLookupServices;
365 }
366 }
367
368 private static ArrayList getServices(DocFlavor flavor,
369 AttributeSet attributes) {
370
371 ArrayList listOfServices = new ArrayList();
372 Iterator psIterator = getAllLookupServices().iterator();
373 while (psIterator.hasNext()) {
374 try {
375 PrintServiceLookup lus = (PrintServiceLookup)psIterator.next();
376 PrintService[] services=null;
377 if (flavor == null && attributes == null) {
378 try {
379 services = lus.getPrintServices();
380 } catch (Throwable tr) {
381 }
382 } else {
383 services = lus.getPrintServices(flavor, attributes);
384 }
385 if (services == null) {
386 continue;
387 }
388 for (int i=0; i<services.length; i++) {
389 listOfServices.add(services[i]);
390 }
391 } catch (Exception e) {
392 }
393 }
394 /* add any directly registered services */
395 ArrayList registeredServices = null;
396 try {
397 SecurityManager security = System.getSecurityManager();
398 if (security != null) {
399 security.checkPrintJobAccess();
400 }
401 registeredServices = getRegisteredServices();
402 } catch (SecurityException se) {
403 }
404 if (registeredServices != null) {
405 PrintService[] services = (PrintService[])
406 registeredServices.toArray(
407 new PrintService[registeredServices.size()]);
408 for (int i=0; i<services.length; i++) {
409 if (!listOfServices.contains(services[i])) {
410 if (flavor == null && attributes == null) {
411 listOfServices.add(services[i]);
412 } else if (((flavor != null &&
413 services[i].isDocFlavorSupported(flavor)) ||
414 flavor == null) &&
415 null == services[i].getUnsupportedAttributes(
416 flavor, attributes)) {
417 listOfServices.add(services[i]);
418 }
419 }
420 }
421 }
422 return listOfServices;
423 }
424
425 private static ArrayList getMultiDocServices(DocFlavor[] flavors,
426 AttributeSet attributes) {
427
428
429 ArrayList listOfServices = new ArrayList();
430 Iterator psIterator = getAllLookupServices().iterator();
431 while (psIterator.hasNext()) {
432 try {
433 PrintServiceLookup lus = (PrintServiceLookup)psIterator.next();
434 MultiDocPrintService[] services =
435 lus.getMultiDocPrintServices(flavors, attributes);
436 if (services == null) {
437 continue;
438 }
439 for (int i=0; i<services.length; i++) {
440 listOfServices.add(services[i]);
441 }
442 } catch (Exception e) {
443 }
444 }
445 /* add any directly registered services */
446 ArrayList registeredServices = null;
447 try {
448 SecurityManager security = System.getSecurityManager();
449 if (security != null) {
450 security.checkPrintJobAccess();
451 }
452 registeredServices = getRegisteredServices();
453 } catch (Exception e) {
454 }
455 if (registeredServices != null) {
456 PrintService[] services = (PrintService[])
457 registeredServices.toArray(
458 new PrintService[registeredServices.size()]);
459 for (int i=0; i<services.length; i++) {
460 if (services[i] instanceof MultiDocPrintService &&
461 !listOfServices.contains(services[i])) {
462 if (flavors == null || flavors.length == 0) {
463 listOfServices.add(services[i]);
464 } else {
465 boolean supported = true;
466 for (int f=0; f<flavors.length; f++) {
467 if (services[i].isDocFlavorSupported(flavors[f])) {
468
469 if (services[i].getUnsupportedAttributes(
470 flavors[f], attributes) != null) {
471 supported = false;
472 break;
473 }
474 } else {
475 supported = false;
476 break;
477 }
478 }
479 if (supported) {
480 listOfServices.add(services[i]);
481 }
482 }
483 }
484 }
485 }
486 return listOfServices;
487 }
488
489 }