Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/sun/print/PrintServiceLookupProvider.java
32287 views
/*1* Copyright (c) 2000, 2019, 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 sun.print;2627import java.security.AccessController;28import java.util.ArrayList;29import java.util.Arrays;30import java.util.Comparator;31import javax.print.DocFlavor;32import javax.print.MultiDocPrintService;33import javax.print.PrintService;34import javax.print.PrintServiceLookup;35import javax.print.attribute.Attribute;36import javax.print.attribute.AttributeSet;37import javax.print.attribute.HashPrintRequestAttributeSet;38import javax.print.attribute.HashPrintServiceAttributeSet;39import javax.print.attribute.PrintRequestAttribute;40import javax.print.attribute.PrintRequestAttributeSet;41import javax.print.attribute.PrintServiceAttribute;42import javax.print.attribute.PrintServiceAttributeSet;43import javax.print.attribute.standard.PrinterName;4445public class PrintServiceLookupProvider extends PrintServiceLookup {4647private String defaultPrinter;48private PrintService defaultPrintService;49private String[] printers; /* excludes the default printer */50private PrintService[] printServices; /* includes the default printer */5152private static final int DEFAULT_REFRESH_TIME = 240; // 4 minutes53private static final int MINIMUM_REFRESH_TIME = 120; // 2 minutes54private static final boolean pollServices;55private static final int refreshTime;5657static {58/* The system property "sun.java2d.print.polling"59* can be used to force the printing code to poll or not poll60* for PrintServices.61*/62String pollStr = java.security.AccessController.doPrivileged(63new sun.security.action.GetPropertyAction("sun.java2d.print.polling"));64pollServices = !("false".equalsIgnoreCase(pollStr));6566/* The system property "sun.java2d.print.minRefreshTime"67* can be used to specify minimum refresh time (in seconds)68* for polling PrintServices. The default is 240.69*/70String refreshTimeStr = java.security.AccessController.doPrivileged(71new sun.security.action.GetPropertyAction(72"sun.java2d.print.minRefreshTime"));73refreshTime = (refreshTimeStr != null)74? getRefreshTime(refreshTimeStr)75: DEFAULT_REFRESH_TIME;7677java.security.AccessController.doPrivileged(78new java.security.PrivilegedAction<Void>() {79public Void run() {80System.loadLibrary("awt");81return null;82}83});84}8586private static int getRefreshTime(final String refreshTimeStr) {87try {88int minRefreshTime = Integer.parseInt(refreshTimeStr);89return (minRefreshTime < MINIMUM_REFRESH_TIME)90? MINIMUM_REFRESH_TIME91: minRefreshTime;92} catch (NumberFormatException e) {93return DEFAULT_REFRESH_TIME;94}95}9697/* The singleton win32 print lookup service.98* Code that is aware of this field and wants to use it must first99* see if its null, and if so instantiate it by calling a method such as100* javax.print.PrintServiceLookup.defaultPrintService() so that the101* same instance is stored there.102*/103private static PrintServiceLookupProvider win32PrintLUS;104105/* Think carefully before calling this. Preferably don't call it. */106public static PrintServiceLookupProvider getWin32PrintLUS() {107if (win32PrintLUS == null) {108/* This call is internally synchronized.109* When it returns an instance of this class will have110* been instantiated - else there's a JDK internal error.111*/112PrintServiceLookup.lookupDefaultPrintService();113}114return win32PrintLUS;115}116117public PrintServiceLookupProvider() {118119if (win32PrintLUS == null) {120win32PrintLUS = this;121122String osName = AccessController.doPrivileged(123new sun.security.action.GetPropertyAction("os.name"));124// There's no capability for Win98 to refresh printers.125// See "OpenPrinter" for more info.126if (osName != null && osName.startsWith("Windows 98")) {127return;128}129// start the local printer listener thread130Thread thr = new PrinterChangeListener();131thr.setDaemon(true);132thr.start();133134if (pollServices) {135// start the remote printer listener thread136Thread remThr = new RemotePrinterChangeListener();137remThr.setDaemon(true);138remThr.start();139}140} /* else condition ought to never happen! */141}142143/* Want the PrintService which is default print service to have144* equality of reference with the equivalent in list of print services145* This isn't required by the API and there's a risk doing this will146* lead people to assume its guaranteed.147*/148public synchronized PrintService[] getPrintServices() {149SecurityManager security = System.getSecurityManager();150if (security != null) {151security.checkPrintJobAccess();152}153if (printServices == null) {154refreshServices();155}156return printServices;157}158159private synchronized void refreshServices() {160printers = getAllPrinterNames();161if (printers == null) {162// In Windows it is safe to assume no default if printers == null so we163// don't get the default.164printServices = new PrintService[0];165return;166}167168PrintService[] newServices = new PrintService[printers.length];169PrintService defService = getDefaultPrintService();170for (int p = 0; p < printers.length; p++) {171if (defService != null &&172printers[p].equals(defService.getName())) {173newServices[p] = defService;174} else {175if (printServices == null) {176newServices[p] = new Win32PrintService(printers[p]);177} else {178int j;179for (j = 0; j < printServices.length; j++) {180if ((printServices[j]!= null) &&181(printers[p].equals(printServices[j].getName()))) {182newServices[p] = printServices[j];183printServices[j] = null;184break;185}186}187if (j == printServices.length) {188newServices[p] = new Win32PrintService(printers[p]);189}190}191}192}193194// Look for deleted services and invalidate these195if (printServices != null) {196for (int j=0; j < printServices.length; j++) {197if ((printServices[j] instanceof Win32PrintService) &&198(!printServices[j].equals(defaultPrintService))) {199((Win32PrintService)printServices[j]).invalidateService();200}201}202}203printServices = newServices;204}205206207public synchronized PrintService getPrintServiceByName(String name) {208209if (name == null || name.equals("")) {210return null;211} else {212/* getPrintServices() is now very fast. */213PrintService[] printServices = getPrintServices();214for (int i=0; i<printServices.length; i++) {215if (printServices[i].getName().equals(name)) {216return printServices[i];217}218}219return null;220}221}222223boolean matchingService(PrintService service,224PrintServiceAttributeSet serviceSet) {225if (serviceSet != null) {226Attribute [] attrs = serviceSet.toArray();227Attribute serviceAttr;228for (int i=0; i<attrs.length; i++) {229serviceAttr230= service.getAttribute((Class<PrintServiceAttribute>)attrs[i].getCategory());231if (serviceAttr == null || !serviceAttr.equals(attrs[i])) {232return false;233}234}235}236return true;237}238239public PrintService[] getPrintServices(DocFlavor flavor,240AttributeSet attributes) {241242SecurityManager security = System.getSecurityManager();243if (security != null) {244security.checkPrintJobAccess();245}246PrintRequestAttributeSet requestSet = null;247PrintServiceAttributeSet serviceSet = null;248249if (attributes != null && !attributes.isEmpty()) {250251requestSet = new HashPrintRequestAttributeSet();252serviceSet = new HashPrintServiceAttributeSet();253254Attribute[] attrs = attributes.toArray();255for (int i=0; i<attrs.length; i++) {256if (attrs[i] instanceof PrintRequestAttribute) {257requestSet.add(attrs[i]);258} else if (attrs[i] instanceof PrintServiceAttribute) {259serviceSet.add(attrs[i]);260}261}262}263264/*265* Special case: If client is asking for a particular printer266* (by name) then we can save time by getting just that service267* to check against the rest of the specified attributes.268*/269PrintService[] services = null;270if (serviceSet != null && serviceSet.get(PrinterName.class) != null) {271PrinterName name = (PrinterName)serviceSet.get(PrinterName.class);272PrintService service = getPrintServiceByName(name.getValue());273if (service == null || !matchingService(service, serviceSet)) {274services = new PrintService[0];275} else {276services = new PrintService[1];277services[0] = service;278}279} else {280services = getPrintServices();281}282283if (services.length == 0) {284return services;285} else {286ArrayList matchingServices = new ArrayList();287for (int i=0; i<services.length; i++) {288try {289if (services[i].290getUnsupportedAttributes(flavor, requestSet) == null) {291matchingServices.add(services[i]);292}293} catch (IllegalArgumentException e) {294}295}296services = new PrintService[matchingServices.size()];297return (PrintService[])matchingServices.toArray(services);298}299}300301/*302* return empty array as don't support multi docs303*/304public MultiDocPrintService[]305getMultiDocPrintServices(DocFlavor[] flavors,306AttributeSet attributes) {307SecurityManager security = System.getSecurityManager();308if (security != null) {309security.checkPrintJobAccess();310}311return new MultiDocPrintService[0];312}313314315public synchronized PrintService getDefaultPrintService() {316SecurityManager security = System.getSecurityManager();317if (security != null) {318security.checkPrintJobAccess();319}320321322// Windows does not have notification for a change in default323// so we always get the latest.324defaultPrinter = getDefaultPrinterName();325if (defaultPrinter == null) {326return null;327}328329if ((defaultPrintService != null) &&330defaultPrintService.getName().equals(defaultPrinter)) {331332return defaultPrintService;333}334335// Not the same as default so proceed to get new PrintService.336337// clear defaultPrintService338defaultPrintService = null;339340if (printServices != null) {341for (int j=0; j<printServices.length; j++) {342if (defaultPrinter.equals(printServices[j].getName())) {343defaultPrintService = printServices[j];344break;345}346}347}348349if (defaultPrintService == null) {350defaultPrintService = new Win32PrintService(defaultPrinter);351}352return defaultPrintService;353}354355class PrinterChangeListener extends Thread {356long chgObj;357PrinterChangeListener() {358chgObj = notifyFirstPrinterChange(null);359}360361public void run() {362if (chgObj != -1) {363while (true) {364// wait for configuration to change365if (notifyPrinterChange(chgObj) != 0) {366try {367refreshServices();368} catch (SecurityException se) {369break;370}371} else {372notifyClosePrinterChange(chgObj);373break;374}375}376}377}378}379380/* Windows provides *PrinterChangeNotification* functions that provides381information about printer status changes of the local printers but not382network printers.383Alternatively, Windows provides a way through which one can get the384network printer status changes by using WMI, RegistryKeyChange combination,385which is a slightly complex mechanism.386The Windows WMI offers an async and sync method to read through registry387via the WQL query. The async method is considered dangerous as it leaves388open a channel until we close it. But the async method has the advantage of389being notified of a change in registry by calling callback without polling for it.390The sync method uses the polling mechanism to notify.391RegistryValueChange cannot be used in combination with WMI to get registry392value change notification because of an error that may be generated because the393scope of the query would be too big to handle(at times).394Hence an alternative mechanism is chosen via the EnumPrinters by polling for the395count of printer status changes(add\remove) and based on it update the printers396list.397*/398class RemotePrinterChangeListener extends Thread implements Comparator<String>{399RemotePrinterChangeListener() {400}401402@Override403public int compare(String o1, String o2) {404return ((o1 == null)405? ((o2 == null) ? 0 : 1)406: ((o2 == null) ? -1 : o1.compareTo(o2)));407}408409@Override410public void run() {411// Init the list of remote printers412String[] prevRemotePrinters = getRemotePrintersNames();413if (prevRemotePrinters != null) {414Arrays.sort(prevRemotePrinters, this);415}416417while (true) {418try {419Thread.sleep(refreshTime * 1000);420} catch (InterruptedException e) {421break;422}423424String[] currentRemotePrinters = getRemotePrintersNames();425if (currentRemotePrinters != null) {426Arrays.sort(currentRemotePrinters, this);427}428if (!Arrays.equals(prevRemotePrinters, currentRemotePrinters)) {429// The list of remote printers got updated,430// so update the cached list printers which431// includes both local and network printers432refreshServices();433434// store the current data for next comparison435prevRemotePrinters = currentRemotePrinters;436}437}438}439}440441private native String getDefaultPrinterName();442private native String[] getAllPrinterNames();443private native long notifyFirstPrinterChange(String printer);444private native void notifyClosePrinterChange(long chgObj);445private native int notifyPrinterChange(long chgObj);446private native String[] getRemotePrintersNames();447}448449450