1 /* 2 * Copyright (c) 2000, 2015, 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 sun.print; 27 28 import java.io.BufferedReader; 29 import java.io.InputStream; 30 import java.io.InputStreamReader; 31 import java.io.IOException; 32 import java.util.ArrayList; 33 import java.security.AccessController; 34 import java.security.PrivilegedActionException; 35 import java.security.PrivilegedExceptionAction; 36 import javax.print.DocFlavor; 37 import javax.print.MultiDocPrintService; 38 import javax.print.PrintService; 39 import javax.print.PrintServiceLookup; 40 import javax.print.attribute.Attribute; 41 import javax.print.attribute.AttributeSet; 42 import javax.print.attribute.HashPrintRequestAttributeSet; 43 import javax.print.attribute.HashPrintServiceAttributeSet; 44 import javax.print.attribute.PrintRequestAttribute; 45 import javax.print.attribute.PrintRequestAttributeSet; 46 import javax.print.attribute.PrintServiceAttribute; 47 import javax.print.attribute.PrintServiceAttributeSet; 48 import javax.print.attribute.standard.PrinterName; 49 50 public class PrintServiceLookupProvider extends PrintServiceLookup { 51 52 private String defaultPrinter; 53 private PrintService defaultPrintService; 54 private String[] printers; /* excludes the default printer */ 55 private PrintService[] printServices; /* includes the default printer */ 56 57 static { 58 java.security.AccessController.doPrivileged( 59 new java.security.PrivilegedAction<Void>() { 60 public Void run() { 61 System.loadLibrary("awt"); 62 return null; 63 } 64 }); 65 } 66 67 /* The singleton win32 print lookup service. 68 * Code that is aware of this field and wants to use it must first 69 * see if its null, and if so instantiate it by calling a method such as 70 * javax.print.PrintServiceLookup.defaultPrintService() so that the 71 * same instance is stored there. 72 */ 73 private static PrintServiceLookupProvider win32PrintLUS; 74 75 /* Think carefully before calling this. Preferably don't call it. */ 76 public static PrintServiceLookupProvider getWin32PrintLUS() { 77 if (win32PrintLUS == null) { 78 /* This call is internally synchronized. 79 * When it returns an instance of this class will have 80 * been instantiated - else there's a JDK internal error. 81 */ 82 PrintServiceLookup.lookupDefaultPrintService(); 83 } 84 return win32PrintLUS; 85 } 86 87 public PrintServiceLookupProvider() { 88 89 if (win32PrintLUS == null) { 90 win32PrintLUS = this; 91 92 String osName = AccessController.doPrivileged( 93 new sun.security.action.GetPropertyAction("os.name")); 94 // There's no capability for Win98 to refresh printers. 95 // See "OpenPrinter" for more info. 96 if (osName != null && osName.startsWith("Windows 98")) { 97 return; 98 } 99 // start the printer listener thread 100 Thread thr = new Thread(null, new PrinterChangeListener(), 101 "PrinterListener", 0, false); 102 thr.setDaemon(true); 103 thr.start(); 104 } /* else condition ought to never happen! */ 105 } 106 107 /* Want the PrintService which is default print service to have 108 * equality of reference with the equivalent in list of print services 109 * This isn't required by the API and there's a risk doing this will 110 * lead people to assume its guaranteed. 111 */ 112 public synchronized PrintService[] getPrintServices() { 113 SecurityManager security = System.getSecurityManager(); 114 if (security != null) { 115 security.checkPrintJobAccess(); 116 } 117 if (printServices == null) { 118 refreshServices(); 119 } 120 return printServices; 121 } 122 123 private synchronized void refreshServices() { 124 printers = getAllPrinterNames(); 125 if (printers == null) { 126 // In Windows it is safe to assume no default if printers == null so we 127 // don't get the default. 128 printServices = new PrintService[0]; 129 return; 130 } 131 132 PrintService[] newServices = new PrintService[printers.length]; 133 PrintService defService = getDefaultPrintService(); 134 for (int p = 0; p < printers.length; p++) { 135 if (defService != null && 136 printers[p].equals(defService.getName())) { 137 newServices[p] = defService; 138 } else { 139 if (printServices == null) { 140 newServices[p] = new Win32PrintService(printers[p]); 141 } else { 142 int j; 143 for (j = 0; j < printServices.length; j++) { 144 if ((printServices[j]!= null) && 145 (printers[p].equals(printServices[j].getName()))) { 146 newServices[p] = printServices[j]; 147 printServices[j] = null; 148 break; 149 } 150 } 151 if (j == printServices.length) { 152 newServices[p] = new Win32PrintService(printers[p]); 153 } 154 } 155 } 156 } 157 158 // Look for deleted services and invalidate these 159 if (printServices != null) { 160 for (int j=0; j < printServices.length; j++) { 161 if ((printServices[j] instanceof Win32PrintService) && 162 (!printServices[j].equals(defaultPrintService))) { 163 ((Win32PrintService)printServices[j]).invalidateService(); 164 } 165 } 166 } 167 printServices = newServices; 168 } 169 170 171 public synchronized PrintService getPrintServiceByName(String name) { 172 173 if (name == null || name.equals("")) { 174 return null; 175 } else { 176 /* getPrintServices() is now very fast. */ 177 PrintService[] printServices = getPrintServices(); 178 for (int i=0; i<printServices.length; i++) { 179 if (printServices[i].getName().equals(name)) { 180 return printServices[i]; 181 } 182 } 183 return null; 184 } 185 } 186 187 @SuppressWarnings("unchecked") // Cast to Class<PrintServiceAttribute> 188 boolean matchingService(PrintService service, 189 PrintServiceAttributeSet serviceSet) { 190 if (serviceSet != null) { 191 Attribute [] attrs = serviceSet.toArray(); 192 Attribute serviceAttr; 193 for (int i=0; i<attrs.length; i++) { 194 serviceAttr 195 = service.getAttribute((Class<PrintServiceAttribute>)attrs[i].getCategory()); 196 if (serviceAttr == null || !serviceAttr.equals(attrs[i])) { 197 return false; 198 } 199 } 200 } 201 return true; 202 } 203 204 public PrintService[] getPrintServices(DocFlavor flavor, 205 AttributeSet attributes) { 206 207 SecurityManager security = System.getSecurityManager(); 208 if (security != null) { 209 security.checkPrintJobAccess(); 210 } 211 PrintRequestAttributeSet requestSet = null; 212 PrintServiceAttributeSet serviceSet = null; 213 214 if (attributes != null && !attributes.isEmpty()) { 215 216 requestSet = new HashPrintRequestAttributeSet(); 217 serviceSet = new HashPrintServiceAttributeSet(); 218 219 Attribute[] attrs = attributes.toArray(); 220 for (int i=0; i<attrs.length; i++) { 221 if (attrs[i] instanceof PrintRequestAttribute) { 222 requestSet.add(attrs[i]); 223 } else if (attrs[i] instanceof PrintServiceAttribute) { 224 serviceSet.add(attrs[i]); 225 } 226 } 227 } 228 229 /* 230 * Special case: If client is asking for a particular printer 231 * (by name) then we can save time by getting just that service 232 * to check against the rest of the specified attributes. 233 */ 234 PrintService[] services = null; 235 if (serviceSet != null && serviceSet.get(PrinterName.class) != null) { 236 PrinterName name = (PrinterName)serviceSet.get(PrinterName.class); 237 PrintService service = getPrintServiceByName(name.getValue()); 238 if (service == null || !matchingService(service, serviceSet)) { 239 services = new PrintService[0]; 240 } else { 241 services = new PrintService[1]; 242 services[0] = service; 243 } 244 } else { 245 services = getPrintServices(); 246 } 247 248 if (services.length == 0) { 249 return services; 250 } else { 251 ArrayList<PrintService> matchingServices = new ArrayList<>(); 252 for (int i=0; i<services.length; i++) { 253 try { 254 if (services[i]. 255 getUnsupportedAttributes(flavor, requestSet) == null) { 256 matchingServices.add(services[i]); 257 } 258 } catch (IllegalArgumentException e) { 259 } 260 } 261 services = new PrintService[matchingServices.size()]; 262 return matchingServices.toArray(services); 263 } 264 } 265 266 /* 267 * return empty array as don't support multi docs 268 */ 269 public MultiDocPrintService[] 270 getMultiDocPrintServices(DocFlavor[] flavors, 271 AttributeSet attributes) { 272 SecurityManager security = System.getSecurityManager(); 273 if (security != null) { 274 security.checkPrintJobAccess(); 275 } 276 return new MultiDocPrintService[0]; 277 } 278 279 280 public synchronized PrintService getDefaultPrintService() { 281 SecurityManager security = System.getSecurityManager(); 282 if (security != null) { 283 security.checkPrintJobAccess(); 284 } 285 286 287 // Windows does not have notification for a change in default 288 // so we always get the latest. 289 defaultPrinter = getDefaultPrinterName(); 290 if (defaultPrinter == null) { 291 return null; 292 } 293 294 if ((defaultPrintService != null) && 295 defaultPrintService.getName().equals(defaultPrinter)) { 296 297 return defaultPrintService; 298 } 299 300 // Not the same as default so proceed to get new PrintService. 301 302 // clear defaultPrintService 303 defaultPrintService = null; 304 305 if (printServices != null) { 306 for (int j=0; j<printServices.length; j++) { 307 if (defaultPrinter.equals(printServices[j].getName())) { 308 defaultPrintService = printServices[j]; 309 break; 310 } 311 } 312 } 313 314 if (defaultPrintService == null) { 315 defaultPrintService = new Win32PrintService(defaultPrinter); 316 } 317 return defaultPrintService; 318 } 319 320 /* Windows provides *PrinterChangeNotification* functions that provides 321 information about printer status changes of the local printers but not 322 network printers. 323 Alternatively, Windows provides a way thro' which one can get the 324 network printer status changes by using WMI, RegistryKeyChange combination, 325 which is a slightly complex mechanism. 326 The Windows WMI offers an async and sync method to read thro' registry 327 via the WQL query. The async method is considered dangerous as it leaves 328 open a channel until we close it. But the async method has the advantage of 329 being notified of a change in registry by calling callback without polling for it. 330 The sync method uses the polling mechanism to notify. 331 RegistryValueChange cannot be used in combination with WMI to get registry 332 value change notification because of an error that may be generated because the 333 scope of the query would be too big to handle(at times). 334 Hence an alternative mechanism is choosen via the EnumPrinters by polling for the 335 count of printer status changes(add\remove) and based on it update the printers 336 list. 337 */ 338 class PrinterChangeListener implements Runnable { 339 long prevRemotePrintersCount = 0; 340 341 PrinterChangeListener() { 342 prevRemotePrintersCount = GetRemotePrintersCount(); 343 } 344 345 @Override 346 public void run() { 347 while(true) { 348 long currentRemotePrintersCount = GetRemotePrintersCount(); 349 if(prevRemotePrintersCount != currentRemotePrintersCount) { 350 351 // updated the printers data 352 // printers list now contains both local and network printer data 353 refreshServices(); 354 355 // store the current data for next comparison 356 prevRemotePrintersCount = currentRemotePrintersCount; 357 } 358 } 359 } 360 } 361 362 private native String getDefaultPrinterName(); 363 private native String[] getAllPrinterNames(); 364 private native long GetRemotePrintersCount(); 365 }