/* * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.print; import java.util.ArrayList; import java.util.Iterator; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import javax.print.attribute.AttributeSet; import sun.awt.AppContext; /** * Implementations of this class provide lookup services for print services * (typically equivalent to printers) of a particular type. *

* Multiple implementations may be installed concurrently. All implementations * must be able to describe the located printers as instances of a * {@code PrintService}. Typically implementations of this service class are * located automatically in JAR files (see the SPI JAR file specification). * These classes must be instantiable using a default constructor. Alternatively * applications may explicitly register instances at runtime. *

* Applications use only the static methods of this abstract class. The instance * methods are implemented by a service provider in a subclass and the * unification of the results from all installed lookup classes are reported by * the static methods of this class when called by the application. *

* A {@code PrintServiceLookup} implementor is recommended to check for the * {@code SecurityManager.checkPrintJobAccess()} to deny access to untrusted * code. Following this recommended policy means that untrusted code may not be * able to locate any print services. Downloaded applets are the most common * example of untrusted code. *

* This check is made on a per lookup service basis to allow flexibility in the * policy to reflect the needs of different lookup services. *

* Services which are registered by {@link #registerService(PrintService)} will * not be included in lookup results if a security manager is installed and its * {@code checkPrintJobAccess()} method denies access. */ public abstract class PrintServiceLookup { /** * Constructor for subclasses to call. */ public PrintServiceLookup() {} /** * Contains a lists of services. */ static class Services { /** * The list of lookup services. */ private ArrayList listOfLookupServices = null; /** * The list of registered services. */ private ArrayList registeredServices = null; } /** * Returns the services from the current appcontext. * * @return the services */ private static Services getServicesForContext() { Services services = (Services)AppContext.getAppContext().get(Services.class); if (services == null) { services = new Services(); AppContext.getAppContext().put(Services.class, services); } return services; } /** * Returns the list of lookup services. * * @return the list of lookup services */ private static ArrayList getListOfLookupServices() { return getServicesForContext().listOfLookupServices; } /** * Initialize the list of lookup services. * * @return the list of lookup services */ private static ArrayList initListOfLookupServices() { ArrayList listOfLookupServices = new ArrayList<>(); getServicesForContext().listOfLookupServices = listOfLookupServices; return listOfLookupServices; } /** * Returns the list of registered services. * * @return the list of registered services */ private static ArrayList getRegisteredServices() { return getServicesForContext().registeredServices; } /** * Initialize the list of registered services. * * @return the list of registered services */ private static ArrayList initRegisteredServices() { ArrayList registeredServices = new ArrayList<>(); getServicesForContext().registeredServices = registeredServices; return registeredServices; } /** * Locates print services capable of printing the specified * {@link DocFlavor}. * * @param flavor the flavor to print. If {@code null}, this constraint is * not used. * @param attributes attributes that the print service must support. If * {@code null} this constraint is not used. * @return array of matching {@code PrintService} objects representing print * services that support the specified flavor attributes. If no * services match, the array is zero-length. */ public static final PrintService[] lookupPrintServices(DocFlavor flavor, AttributeSet attributes) { ArrayList list = getServices(flavor, attributes); return list.toArray(new PrintService[list.size()]); } /** * Locates {@code MultiDoc} print {@code Services} capable of printing * {@code MultiDocs} containing all the specified doc flavors. *

* This method is useful to help locate a service that can print a * {@code MultiDoc} in which the elements may be different flavors. An * application could perform this itself by multiple lookups on each * {@code DocFlavor} in turn and collating the results, but the lookup * service may be able to do this more efficiently. * * @param flavors the flavors to print. If {@code null} or empty this * constraint is not used. Otherwise return only multidoc print * services that can print all specified doc flavors. * @param attributes attributes that the print service must support. If * {@code null} this constraint is not used. * @return array of matching {@link MultiDocPrintService} objects. If no * services match, the array is zero-length. */ public static final MultiDocPrintService[] lookupMultiDocPrintServices(DocFlavor[] flavors, AttributeSet attributes) { ArrayList list = getMultiDocServices(flavors, attributes); return list.toArray(new MultiDocPrintService[list.size()]); } /** * Locates the default print service for this environment. This may return * {@code null}. If multiple lookup services each specify a default, the * chosen service is not precisely defined, but a platform native service, * rather than an installed service, is usually returned as the default. If * there is no clearly identifiable platform native default print service, * the default is the first to be located in an implementation-dependent * manner. *

* This may include making use of any preferences API that is available as * part of the Java or native platform. This algorithm may be overridden by * a user setting the property {@code javax.print.defaultPrinter}. A service * specified must be discovered to be valid and currently available to be * returned as the default. * * @return the default {@code PrintService} */ public static final PrintService lookupDefaultPrintService() { Iterator psIterator = getAllLookupServices().iterator(); while (psIterator.hasNext()) { try { PrintServiceLookup lus = psIterator.next(); PrintService service = lus.getDefaultPrintService(); if (service != null) { return service; } } catch (Exception e) { } } return null; } /** * Allows an application to explicitly register a class that implements * lookup services. The registration will not persist across VM invocations. * This is useful if an application needs to make a new service available * that is not part of the installation. If the lookup service is already * registered, or cannot be registered, the method returns {@code false}. * * @param sp an implementation of a lookup service * @return {@code true} if the new lookup service is newly registered; * {@code false} otherwise */ public static boolean registerServiceProvider(PrintServiceLookup sp) { synchronized (PrintServiceLookup.class) { Iterator psIterator = getAllLookupServices().iterator(); while (psIterator.hasNext()) { try { Object lus = psIterator.next(); if (lus.getClass() == sp.getClass()) { return false; } } catch (Exception e) { } } getListOfLookupServices().add(sp); return true; } } /** * Allows an application to directly register an instance of a class which * implements a print service. The lookup operations for this service will * be performed by the {@code PrintServiceLookup} class using the attribute * values and classes reported by the service. This may be less efficient * than a lookup service tuned for that service. Therefore registering a * {@code PrintServiceLookup} instance instead is recommended. The method * returns {@code true} if this service is not previously registered and is * now successfully registered. This method should not be called with * {@code StreamPrintService} instances. They will always fail to register * and the method will return {@code false}. * * @param service an implementation of a print service * @return {@code true} if the service is newly registered; {@code false} * otherwise */ public static boolean registerService(PrintService service) { synchronized (PrintServiceLookup.class) { if (service == null || service instanceof StreamPrintService) { return false; } ArrayList registeredServices = getRegisteredServices(); if (registeredServices == null) { registeredServices = initRegisteredServices(); } else { if (registeredServices.contains(service)) { return false; } } registeredServices.add(service); return true; } } /** * Locates services that can be positively confirmed to support the * combination of attributes and {@code DocFlavors} specified. This method * is not called directly by applications. *

* Implemented by a service provider, used by the static methods of this * class. *

* The results should be the same as obtaining all the {@code PrintServices} * and querying each one individually on its support for the specified * attributes and flavors, but the process can be more efficient by taking * advantage of the capabilities of lookup services for the print services. * * @param flavor of document required. If {@code null} it is ignored. * @param attributes required to be supported. If {@code null} this * constraint is not used. * @return array of matching {@code PrintServices}. If no services match, * the array is zero-length. */ public abstract PrintService[] getPrintServices(DocFlavor flavor, AttributeSet attributes); /** * Not called directly by applications. Implemented by a service provider, * used by the static methods of this class. * * @return array of all {@code PrintServices} known to this lookup service * class. If none are found, the array is zero-length. */ public abstract PrintService[] getPrintServices() ; /** * Not called directly by applications. *

* Implemented by a service provider, used by the static methods of this * class. *

* Locates {@code MultiDoc} print services which can be positively confirmed * to support the combination of attributes and {@code DocFlavors} * specified. * * @param flavors of documents required. If {@code null} or empty it is * ignored. * @param attributes required to be supported. If {@code null} this * constraint is not used. * @return array of matching {@code PrintServices}. If no services match, * the array is zero-length. */ public abstract MultiDocPrintService[] getMultiDocPrintServices(DocFlavor[] flavors, AttributeSet attributes); /** * Not called directly by applications. Implemented by a service provider, * and called by the print lookup service. * * @return the default {@code PrintService} for this lookup service. If * there is no default, returns {@code null}. */ public abstract PrintService getDefaultPrintService(); /** * Returns all lookup services for this environment. * * @return all lookup services for this environment */ private static ArrayList getAllLookupServices() { synchronized (PrintServiceLookup.class) { ArrayList listOfLookupServices = getListOfLookupServices(); if (listOfLookupServices != null) { return listOfLookupServices; } else { listOfLookupServices = initListOfLookupServices(); } try { java.security.AccessController.doPrivileged( new java.security.PrivilegedExceptionAction() { public Object run() { Iterator iterator = ServiceLoader.load(PrintServiceLookup.class). iterator(); ArrayList los = getListOfLookupServices(); while (iterator.hasNext()) { try { los.add(iterator.next()); } catch (ServiceConfigurationError err) { /* In the applet case, we continue */ if (System.getSecurityManager() != null) { err.printStackTrace(); } else { throw err; } } } return null; } }); } catch (java.security.PrivilegedActionException e) { } return listOfLookupServices; } } /** * Locates print services capable of printing the specified * {@link DocFlavor}. * * @param flavor the flavor to print. If {@code null}, this constraint is * not used. * @param attributes attributes that the print service must support. If * {@code null} this constraint is not used. * @return list of matching {@code PrintService} objects representing print * services that support the specified flavor attributes. If no * services match, the empty list is returned. */ private static ArrayList getServices(DocFlavor flavor, AttributeSet attributes) { ArrayList listOfServices = new ArrayList<>(); Iterator psIterator = getAllLookupServices().iterator(); while (psIterator.hasNext()) { try { PrintServiceLookup lus = psIterator.next(); PrintService[] services=null; if (flavor == null && attributes == null) { try { services = lus.getPrintServices(); } catch (Throwable tr) { } } else { services = lus.getPrintServices(flavor, attributes); } if (services == null) { continue; } for (int i=0; i registeredServices = null; try { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPrintJobAccess(); } registeredServices = getRegisteredServices(); } catch (SecurityException se) { } if (registeredServices != null) { PrintService[] services = registeredServices.toArray( new PrintService[registeredServices.size()]); for (int i=0; i getMultiDocServices(DocFlavor[] flavors, AttributeSet attributes) { ArrayList listOfServices = new ArrayList<>(); Iterator psIterator = getAllLookupServices().iterator(); while (psIterator.hasNext()) { try { PrintServiceLookup lus = psIterator.next(); MultiDocPrintService[] services = lus.getMultiDocPrintServices(flavors, attributes); if (services == null) { continue; } for (int i=0; i registeredServices = null; try { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPrintJobAccess(); } registeredServices = getRegisteredServices(); } catch (Exception e) { } if (registeredServices != null) { PrintService[] services = registeredServices.toArray(new PrintService[registeredServices.size()]); for (int i=0; i