Path: blob/master/src/java.desktop/share/classes/javax/print/PrintServiceLookup.java
41153 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.print;2627import java.util.ArrayList;28import java.util.Iterator;29import java.util.ServiceConfigurationError;30import java.util.ServiceLoader;3132import javax.print.attribute.AttributeSet;3334import sun.awt.AppContext;3536/**37* Implementations of this class provide lookup services for print services38* (typically equivalent to printers) of a particular type.39* <p>40* Multiple implementations may be installed concurrently. All implementations41* must be able to describe the located printers as instances of a42* {@code PrintService}. Typically implementations of this service class are43* located automatically in JAR files (see the SPI JAR file specification).44* These classes must be instantiable using a default constructor. Alternatively45* applications may explicitly register instances at runtime.46* <p>47* Applications use only the static methods of this abstract class. The instance48* methods are implemented by a service provider in a subclass and the49* unification of the results from all installed lookup classes are reported by50* the static methods of this class when called by the application.51* <p>52* A {@code PrintServiceLookup} implementor is recommended to check for the53* {@code SecurityManager.checkPrintJobAccess()} to deny access to untrusted54* code. Following this recommended policy means that untrusted code may not be55* able to locate any print services. Downloaded applets are the most common56* example of untrusted code.57* <p>58* This check is made on a per lookup service basis to allow flexibility in the59* policy to reflect the needs of different lookup services.60* <p>61* Services which are registered by {@link #registerService(PrintService)} will62* not be included in lookup results if a security manager is installed and its63* {@code checkPrintJobAccess()} method denies access.64*/65public abstract class PrintServiceLookup {6667/**68* Constructor for subclasses to call.69*/70protected PrintServiceLookup() {}7172/**73* Contains a lists of services.74*/75static class Services {7677/**78* The list of lookup services.79*/80private ArrayList<PrintServiceLookup> listOfLookupServices = null;8182/**83* The list of registered services.84*/85private ArrayList<PrintService> registeredServices = null;86}8788/**89* Returns the services from the current appcontext.90*91* @return the services92*/93private static Services getServicesForContext() {94Services services =95(Services)AppContext.getAppContext().get(Services.class);96if (services == null) {97services = new Services();98AppContext.getAppContext().put(Services.class, services);99}100return services;101}102103/**104* Returns the list of lookup services.105*106* @return the list of lookup services107*/108private static ArrayList<PrintServiceLookup> getListOfLookupServices() {109return getServicesForContext().listOfLookupServices;110}111112/**113* Initialize the list of lookup services.114*115* @return the list of lookup services116*/117private static ArrayList<PrintServiceLookup> initListOfLookupServices() {118ArrayList<PrintServiceLookup> listOfLookupServices = new ArrayList<>();119getServicesForContext().listOfLookupServices = listOfLookupServices;120return listOfLookupServices;121}122123/**124* Returns the list of registered services.125*126* @return the list of registered services127*/128private static ArrayList<PrintService> getRegisteredServices() {129return getServicesForContext().registeredServices;130}131132/**133* Initialize the list of registered services.134*135* @return the list of registered services136*/137private static ArrayList<PrintService> initRegisteredServices() {138ArrayList<PrintService> registeredServices = new ArrayList<>();139getServicesForContext().registeredServices = registeredServices;140return registeredServices;141}142143/**144* Locates print services capable of printing the specified145* {@link DocFlavor}.146*147* @param flavor the flavor to print. If {@code null}, this constraint is148* not used.149* @param attributes attributes that the print service must support. If150* {@code null} this constraint is not used.151* @return array of matching {@code PrintService} objects representing print152* services that support the specified flavor attributes. If no153* services match, the array is zero-length.154*/155public static final PrintService[]156lookupPrintServices(DocFlavor flavor,157AttributeSet attributes) {158ArrayList<PrintService> list = getServices(flavor, attributes);159return list.toArray(new PrintService[list.size()]);160}161162/**163* Locates {@code MultiDoc} print {@code Services} capable of printing164* {@code MultiDocs} containing all the specified doc flavors.165* <p>166* This method is useful to help locate a service that can print a167* {@code MultiDoc} in which the elements may be different flavors. An168* application could perform this itself by multiple lookups on each169* {@code DocFlavor} in turn and collating the results, but the lookup170* service may be able to do this more efficiently.171*172* @param flavors the flavors to print. If {@code null} or empty this173* constraint is not used. Otherwise return only multidoc print174* services that can print all specified doc flavors.175* @param attributes attributes that the print service must support. If176* {@code null} this constraint is not used.177* @return array of matching {@link MultiDocPrintService} objects. If no178* services match, the array is zero-length.179*/180public static final MultiDocPrintService[]181lookupMultiDocPrintServices(DocFlavor[] flavors,182AttributeSet attributes) {183ArrayList<MultiDocPrintService> list = getMultiDocServices(flavors, attributes);184return list.toArray(new MultiDocPrintService[list.size()]);185}186187/**188* Locates the default print service for this environment. This may return189* {@code null}. If multiple lookup services each specify a default, the190* chosen service is not precisely defined, but a platform native service,191* rather than an installed service, is usually returned as the default. If192* there is no clearly identifiable platform native default print service,193* the default is the first to be located in an implementation-dependent194* manner.195* <p>196* This may include making use of any preferences API that is available as197* part of the Java or native platform. This algorithm may be overridden by198* a user setting the property {@code javax.print.defaultPrinter}. A service199* specified must be discovered to be valid and currently available to be200* returned as the default.201*202* @return the default {@code PrintService}203*/204public static final PrintService lookupDefaultPrintService() {205206Iterator<PrintServiceLookup> psIterator = getAllLookupServices().iterator();207while (psIterator.hasNext()) {208try {209PrintServiceLookup lus = psIterator.next();210PrintService service = lus.getDefaultPrintService();211if (service != null) {212return service;213}214} catch (Exception e) {215}216}217return null;218}219220/**221* Allows an application to explicitly register a class that implements222* lookup services. The registration will not persist across VM invocations.223* This is useful if an application needs to make a new service available224* that is not part of the installation. If the lookup service is already225* registered, or cannot be registered, the method returns {@code false}.226*227* @param sp an implementation of a lookup service228* @return {@code true} if the new lookup service is newly registered;229* {@code false} otherwise230*/231public static boolean registerServiceProvider(PrintServiceLookup sp) {232synchronized (PrintServiceLookup.class) {233Iterator<PrintServiceLookup> psIterator =234getAllLookupServices().iterator();235while (psIterator.hasNext()) {236try {237Object lus = psIterator.next();238if (lus.getClass() == sp.getClass()) {239return false;240}241} catch (Exception e) {242}243}244getListOfLookupServices().add(sp);245return true;246}247}248249/**250* Allows an application to directly register an instance of a class which251* implements a print service. The lookup operations for this service will252* be performed by the {@code PrintServiceLookup} class using the attribute253* values and classes reported by the service. This may be less efficient254* than a lookup service tuned for that service. Therefore registering a255* {@code PrintServiceLookup} instance instead is recommended. The method256* returns {@code true} if this service is not previously registered and is257* now successfully registered. This method should not be called with258* {@code StreamPrintService} instances. They will always fail to register259* and the method will return {@code false}.260*261* @param service an implementation of a print service262* @return {@code true} if the service is newly registered; {@code false}263* otherwise264*/265public static boolean registerService(PrintService service) {266synchronized (PrintServiceLookup.class) {267if (service == null || service instanceof StreamPrintService) {268return false;269}270ArrayList<PrintService> registeredServices = getRegisteredServices();271if (registeredServices == null) {272registeredServices = initRegisteredServices();273}274else {275if (registeredServices.contains(service)) {276return false;277}278}279registeredServices.add(service);280return true;281}282}283284/**285* Locates services that can be positively confirmed to support the286* combination of attributes and {@code DocFlavors} specified. This method287* is not called directly by applications.288* <p>289* Implemented by a service provider, used by the static methods of this290* class.291* <p>292* The results should be the same as obtaining all the {@code PrintServices}293* and querying each one individually on its support for the specified294* attributes and flavors, but the process can be more efficient by taking295* advantage of the capabilities of lookup services for the print services.296*297* @param flavor of document required. If {@code null} it is ignored.298* @param attributes required to be supported. If {@code null} this299* constraint is not used.300* @return array of matching {@code PrintServices}. If no services match,301* the array is zero-length.302*/303public abstract PrintService[] getPrintServices(DocFlavor flavor,304AttributeSet attributes);305306/**307* Not called directly by applications. Implemented by a service provider,308* used by the static methods of this class.309*310* @return array of all {@code PrintServices} known to this lookup service311* class. If none are found, the array is zero-length.312*/313public abstract PrintService[] getPrintServices() ;314315/**316* Not called directly by applications.317* <p>318* Implemented by a service provider, used by the static methods of this319* class.320* <p>321* Locates {@code MultiDoc} print services which can be positively confirmed322* to support the combination of attributes and {@code DocFlavors}323* specified.324*325* @param flavors of documents required. If {@code null} or empty it is326* ignored.327* @param attributes required to be supported. If {@code null} this328* constraint is not used.329* @return array of matching {@code PrintServices}. If no services match,330* the array is zero-length.331*/332public abstract MultiDocPrintService[]333getMultiDocPrintServices(DocFlavor[] flavors,334AttributeSet attributes);335336/**337* Not called directly by applications. Implemented by a service provider,338* and called by the print lookup service.339*340* @return the default {@code PrintService} for this lookup service. If341* there is no default, returns {@code null}.342*/343public abstract PrintService getDefaultPrintService();344345/**346* Returns all lookup services for this environment.347*348* @return all lookup services for this environment349*/350@SuppressWarnings("removal")351private static ArrayList<PrintServiceLookup> getAllLookupServices() {352synchronized (PrintServiceLookup.class) {353ArrayList<PrintServiceLookup> listOfLookupServices = getListOfLookupServices();354if (listOfLookupServices != null) {355return listOfLookupServices;356} else {357listOfLookupServices = initListOfLookupServices();358}359try {360java.security.AccessController.doPrivileged(361new java.security.PrivilegedExceptionAction<Object>() {362public Object run() {363Iterator<PrintServiceLookup> iterator =364ServiceLoader.load(PrintServiceLookup.class).365iterator();366ArrayList<PrintServiceLookup> los = getListOfLookupServices();367while (iterator.hasNext()) {368try {369los.add(iterator.next());370} catch (ServiceConfigurationError err) {371/* In the applet case, we continue */372if (System.getSecurityManager() != null) {373err.printStackTrace();374} else {375throw err;376}377}378}379return null;380}381});382} catch (java.security.PrivilegedActionException e) {383}384385return listOfLookupServices;386}387}388389/**390* Locates print services capable of printing the specified391* {@link DocFlavor}.392*393* @param flavor the flavor to print. If {@code null}, this constraint is394* not used.395* @param attributes attributes that the print service must support. If396* {@code null} this constraint is not used.397* @return list of matching {@code PrintService} objects representing print398* services that support the specified flavor attributes. If no399* services match, the empty list is returned.400*/401private static ArrayList<PrintService> getServices(DocFlavor flavor,402AttributeSet attributes) {403404ArrayList<PrintService> listOfServices = new ArrayList<>();405Iterator<PrintServiceLookup> psIterator = getAllLookupServices().iterator();406while (psIterator.hasNext()) {407try {408PrintServiceLookup lus = psIterator.next();409PrintService[] services=null;410if (flavor == null && attributes == null) {411try {412services = lus.getPrintServices();413} catch (Throwable tr) {414}415} else {416services = lus.getPrintServices(flavor, attributes);417}418if (services == null) {419continue;420}421for (int i=0; i<services.length; i++) {422listOfServices.add(services[i]);423}424} catch (Exception e) {425}426}427/*428* add any directly registered services429*/430ArrayList<PrintService> registeredServices = null;431try {432@SuppressWarnings("removal")433SecurityManager security = System.getSecurityManager();434if (security != null) {435security.checkPrintJobAccess();436}437registeredServices = getRegisteredServices();438} catch (SecurityException se) {439}440if (registeredServices != null) {441PrintService[] services = registeredServices.toArray(442new PrintService[registeredServices.size()]);443for (int i=0; i<services.length; i++) {444if (!listOfServices.contains(services[i])) {445if (flavor == null && attributes == null) {446listOfServices.add(services[i]);447} else if (((flavor != null &&448services[i].isDocFlavorSupported(flavor)) ||449flavor == null) &&450null == services[i].getUnsupportedAttributes(451flavor, attributes)) {452listOfServices.add(services[i]);453}454}455}456}457return listOfServices;458}459460/**461* Locates {@code MultiDoc} print {@code Services} capable of printing462* {@code MultiDocs} containing all the specified doc flavors.463*464* @param flavors the flavors to print. If {@code null} or empty this465* constraint is not used. Otherwise return only multidoc print466* services that can print all specified doc flavors.467* @param attributes attributes that the print service must support. If468* {@code null} this constraint is not used.469* @return list of matching {@link MultiDocPrintService} objects. If no470* services match, the empty list is returned.471*/472private static ArrayList<MultiDocPrintService> getMultiDocServices(DocFlavor[] flavors,473AttributeSet attributes) {474475476ArrayList<MultiDocPrintService> listOfServices = new ArrayList<>();477Iterator<PrintServiceLookup> psIterator = getAllLookupServices().iterator();478while (psIterator.hasNext()) {479try {480PrintServiceLookup lus = psIterator.next();481MultiDocPrintService[] services =482lus.getMultiDocPrintServices(flavors, attributes);483if (services == null) {484continue;485}486for (int i=0; i<services.length; i++) {487listOfServices.add(services[i]);488}489} catch (Exception e) {490}491}492/*493* add any directly registered services494*/495ArrayList<PrintService> registeredServices = null;496try {497@SuppressWarnings("removal")498SecurityManager security = System.getSecurityManager();499if (security != null) {500security.checkPrintJobAccess();501}502registeredServices = getRegisteredServices();503} catch (Exception e) {504}505if (registeredServices != null) {506PrintService[] services =507registeredServices.toArray(new PrintService[registeredServices.size()]);508for (int i=0; i<services.length; i++) {509if (services[i] instanceof MultiDocPrintService &&510!listOfServices.contains(services[i])) {511if (flavors == null || flavors.length == 0) {512listOfServices.add((MultiDocPrintService)services[i]);513} else {514boolean supported = true;515for (int f=0; f<flavors.length; f++) {516if (services[i].isDocFlavorSupported(flavors[f])) {517518if (services[i].getUnsupportedAttributes(519flavors[f], attributes) != null) {520supported = false;521break;522}523} else {524supported = false;525break;526}527}528if (supported) {529listOfServices.add((MultiDocPrintService)services[i]);530}531}532}533}534}535return listOfServices;536}537}538539540