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