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 }