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