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 listOfLookupServices = null; 73 private ArrayList 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 getListOfLookupServices() { 87 return getServicesForContext().listOfLookupServices; 88 } 89 90 private static ArrayList initListOfLookupServices() { 91 ArrayList listOfLookupServices = new ArrayList(); 92 getServicesForContext().listOfLookupServices = listOfLookupServices; 93 return listOfLookupServices; 94 } 95 96 97 private static ArrayList getRegisteredServices() { 98 return getServicesForContext().registeredServices; 99 } 100 101 private static ArrayList initRegisteredServices() { 102 ArrayList 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 list = getServices(flavor, attributes); 124 return (PrintService[])(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 list = getMultiDocServices(flavors, attributes); 152 return (MultiDocPrintService[]) 153 list.toArray(new MultiDocPrintService[list.size()]); 154 } 155 156 157 /** 158 * Locates the default print service for this environment. 159 * This may return null. 160 * If multiple lookup services each specify a default, the 161 * chosen service is not precisely defined, but a 162 * platform native service, rather than an installed service, 163 * is usually returned as the default. If there is no clearly 164 * identifiable 165 * platform native default print service, the default is the first 166 * to be located in an implementation-dependent manner. 167 * <p> 168 * This may include making use of any preferences API that is available 169 * as part of the Java or native platform. 170 * This algorithm may be overridden by a user setting the property 171 * javax.print.defaultPrinter. 172 * A service specified must be discovered to be valid and currently 173 * available to be returned as the default. 174 * 175 * @return the default PrintService. 176 */ 177 178 public static final PrintService lookupDefaultPrintService() { 179 180 Iterator psIterator = getAllLookupServices().iterator(); 181 while (psIterator.hasNext()) { 182 try { 183 PrintServiceLookup lus = (PrintServiceLookup)psIterator.next(); 184 PrintService service = lus.getDefaultPrintService(); 185 if (service != null) { 186 return service; 187 } 188 } catch (Exception e) { 189 } 190 } 191 return null; 192 } 193 194 195 /** 196 * Allows an application to explicitly register a class that 197 * implements lookup services. The registration will not persist 198 * across VM invocations. 199 * This is useful if an application needs to make a new service 200 * available that is not part of the installation. 201 * If the lookup service is already registered, or cannot be registered, 202 * the method returns false. 203 * <p> 204 * 205 * @param sp an implementation of a lookup service. 206 * @return <code>true</code> if the new lookup service is newly 207 * registered; <code>false</code> otherwise. 208 */ 209 public static boolean registerServiceProvider(PrintServiceLookup sp) { 210 synchronized (PrintServiceLookup.class) { 211 Iterator psIterator = getAllLookupServices().iterator(); 212 while (psIterator.hasNext()) { 213 try { 214 Object lus = psIterator.next(); 215 if (lus.getClass() == sp.getClass()) { 216 return false; 217 } 218 } catch (Exception e) { 219 } 220 } 221 getListOfLookupServices().add(sp); 222 return true; 223 } 224 225 } 226 227 228 /** 229 * Allows an application to directly register an instance of a 230 * class which implements a print service. 231 * The lookup operations for this service will be 232 * performed by the PrintServiceLookup class using the attribute 233 * values and classes reported by the service. 234 * This may be less efficient than a lookup 235 * service tuned for that service. 236 * Therefore registering a <code>PrintServiceLookup</code> instance 237 * instead is recommended. 238 * The method returns true if this service is not previously 239 * registered and is now successfully registered. 240 * This method should not be called with StreamPrintService instances. 241 * They will always fail to register and the method will return false. 242 * @param service an implementation of a print service. 243 * @return <code>true</code> if the service is newly 244 * registered; <code>false</code> otherwise. 245 */ 246 247 public static boolean registerService(PrintService service) { 248 synchronized (PrintServiceLookup.class) { 249 if (service instanceof StreamPrintService) { 250 return false; 251 } 252 ArrayList registeredServices = getRegisteredServices(); 253 if (registeredServices == null) { 254 registeredServices = initRegisteredServices(); 255 } 256 else { 257 if (registeredServices.contains(service)) { 258 return false; 259 } 260 } 261 registeredServices.add(service); 262 return true; 263 } 264 } 265 266 267 /** 268 * Locates services that can be positively confirmed to support 269 * the combination of attributes and DocFlavors specified. 270 * This method is not called directly by applications. 271 * <p> 272 * Implemented by a service provider, used by the static methods 273 * of this class. 274 * <p> 275 * The results should be the same as obtaining all the PrintServices 276 * and querying each one individually on its support for the 277 * specified attributes and flavors, but the process can be more 278 * efficient by taking advantage of the capabilities of lookup services 279 * for the print services. 280 * 281 * @param flavor of document required. If null it is ignored. 282 * @param attributes required to be supported. If null this 283 * constraint is not used. 284 * @return array of matching PrintServices. If no services match, the 285 * array is zero-length. 286 */ 287 public abstract PrintService[] getPrintServices(DocFlavor flavor, 288 AttributeSet attributes); 289 290 /** 291 * Not called directly by applications. 292 * Implemented by a service provider, used by the static methods 293 * of this class. 294 * @return array of all PrintServices known to this lookup service 295 * class. If none are found, the array is zero-length. 296 */ 297 public abstract PrintService[] getPrintServices() ; 298 299 300 /** 301 * Not called directly by applications. 302 * <p> 303 * Implemented by a service provider, used by the static methods 304 * of this class. 305 * <p> 306 * Locates MultiDoc print services which can be positively confirmed 307 * to support the combination of attributes and DocFlavors specified. 308 * <p> 309 * 310 * @param flavors of documents required. If null or empty it is ignored. 311 * @param attributes required to be supported. If null this 312 * constraint is not used. 313 * @return array of matching PrintServices. If no services match, the 314 * array is zero-length. 315 */ 316 public abstract MultiDocPrintService[] 317 getMultiDocPrintServices(DocFlavor[] flavors, 318 AttributeSet attributes); 319 320 /** 321 * Not called directly by applications. 322 * Implemented by a service provider, and called by the print lookup 323 * service 324 * @return the default PrintService for this lookup service. 325 * If there is no default, returns null. 326 */ 327 public abstract PrintService getDefaultPrintService(); 328 329 private static ArrayList getAllLookupServices() { 330 synchronized (PrintServiceLookup.class) { 331 ArrayList listOfLookupServices = getListOfLookupServices(); 332 if (listOfLookupServices != null) { 333 return listOfLookupServices; 334 } else { 335 listOfLookupServices = initListOfLookupServices(); 336 } 337 try { 338 java.security.AccessController.doPrivileged( 339 new java.security.PrivilegedExceptionAction() { 340 public Object run() { 341 Iterator<PrintServiceLookup> iterator = 342 ServiceLoader.load(PrintServiceLookup.class). 343 iterator(); 344 ArrayList los = getListOfLookupServices(); 345 while (iterator.hasNext()) { 346 try { 347 los.add(iterator.next()); 348 } catch (ServiceConfigurationError err) { 349 /* In the applet case, we continue */ 350 if (System.getSecurityManager() != null) { 351 err.printStackTrace(); 352 } else { 353 throw err; 354 } 355 } 356 } 357 return null; 358 } 359 }); 360 } catch (java.security.PrivilegedActionException e) { 361 } 362 363 return listOfLookupServices; 364 } 365 } 366 367 private static ArrayList getServices(DocFlavor flavor, 368 AttributeSet attributes) { 369 370 ArrayList listOfServices = new ArrayList(); 371 Iterator psIterator = getAllLookupServices().iterator(); 372 while (psIterator.hasNext()) { 373 try { 374 PrintServiceLookup lus = (PrintServiceLookup)psIterator.next(); 375 PrintService[] services=null; 376 if (flavor == null && attributes == null) { 377 try { 378 services = lus.getPrintServices(); 379 } catch (Throwable tr) { 380 } 381 } else { 382 services = lus.getPrintServices(flavor, attributes); 383 } 384 if (services == null) { 385 continue; 386 } 387 for (int i=0; i<services.length; i++) { 388 listOfServices.add(services[i]); 389 } 390 } catch (Exception e) { 391 } 392 } 393 /* add any directly registered services */ 394 ArrayList registeredServices = null; 395 try { 396 SecurityManager security = System.getSecurityManager(); 397 if (security != null) { 398 security.checkPrintJobAccess(); 399 } 400 registeredServices = getRegisteredServices(); 401 } catch (SecurityException se) { 402 } 403 if (registeredServices != null) { 404 PrintService[] services = (PrintService[]) 405 registeredServices.toArray( 406 new PrintService[registeredServices.size()]); 407 for (int i=0; i<services.length; i++) { 408 if (!listOfServices.contains(services[i])) { 409 if (flavor == null && attributes == null) { 410 listOfServices.add(services[i]); 411 } else if (((flavor != null && 412 services[i].isDocFlavorSupported(flavor)) || 413 flavor == null) && 414 null == services[i].getUnsupportedAttributes( 415 flavor, attributes)) { 416 listOfServices.add(services[i]); 417 } 418 } 419 } 420 } 421 return listOfServices; 422 } 423 424 private static ArrayList getMultiDocServices(DocFlavor[] flavors, 425 AttributeSet attributes) { 426 427 428 ArrayList listOfServices = new ArrayList(); 429 Iterator psIterator = getAllLookupServices().iterator(); 430 while (psIterator.hasNext()) { 431 try { 432 PrintServiceLookup lus = (PrintServiceLookup)psIterator.next(); 433 MultiDocPrintService[] services = 434 lus.getMultiDocPrintServices(flavors, attributes); 435 if (services == null) { 436 continue; 437 } 438 for (int i=0; i<services.length; i++) { 439 listOfServices.add(services[i]); 440 } 441 } catch (Exception e) { 442 } 443 } 444 /* add any directly registered services */ 445 ArrayList registeredServices = null; 446 try { 447 SecurityManager security = System.getSecurityManager(); 448 if (security != null) { 449 security.checkPrintJobAccess(); 450 } 451 registeredServices = getRegisteredServices(); 452 } catch (Exception e) { 453 } 454 if (registeredServices != null) { 455 PrintService[] services = (PrintService[]) 456 registeredServices.toArray( 457 new PrintService[registeredServices.size()]); 458 for (int i=0; i<services.length; i++) { 459 if (services[i] instanceof MultiDocPrintService && 460 !listOfServices.contains(services[i])) { 461 if (flavors == null || flavors.length == 0) { 462 listOfServices.add(services[i]); 463 } else { 464 boolean supported = true; 465 for (int f=0; f<flavors.length; f++) { 466 if (services[i].isDocFlavorSupported(flavors[f])) { 467 468 if (services[i].getUnsupportedAttributes( 469 flavors[f], attributes) != null) { 470 supported = false; 471 break; 472 } 473 } else { 474 supported = false; 475 break; 476 } 477 } 478 if (supported) { 479 listOfServices.add(services[i]); 480 } 481 } 482 } 483 } 484 } 485 return listOfServices; 486 } 487 488 }