| /* | |
| * Copyright (c) 2000, 2024, 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 sun.print; | |
| import java.io.BufferedReader; | |
| import java.io.IOException; | |
| import java.net.MalformedURLException; | |
| import java.util.ArrayList; | |
| import java.util.Vector; | |
| import javax.print.DocFlavor; | |
| import javax.print.MultiDocPrintService; | |
| import javax.print.PrintService; | |
| import javax.print.PrintServiceLookup; | |
| import javax.print.attribute.Attribute; | |
| import javax.print.attribute.AttributeSet; | |
| import javax.print.attribute.HashPrintRequestAttributeSet; | |
| import javax.print.attribute.HashPrintServiceAttributeSet; | |
| import javax.print.attribute.PrintRequestAttribute; | |
| import javax.print.attribute.PrintRequestAttributeSet; | |
| import javax.print.attribute.PrintServiceAttribute; | |
| import javax.print.attribute.PrintServiceAttributeSet; | |
| import javax.print.attribute.standard.PrinterName; | |
| import javax.print.attribute.standard.PrinterURI; | |
| import java.io.File; | |
| import java.io.FileReader; | |
| import java.net.URL; | |
| import java.nio.file.Files; | |
| import sun.awt.OSInfo; | |
| import sun.awt.util.ThreadGroupUtils; | |
| /* | |
| * Remind: This class uses solaris commands. We also need a linux | |
| * version | |
| */ | |
| public final class PrintServiceLookupProvider extends PrintServiceLookup | |
| implements BackgroundServiceLookup, Runnable { | |
| /* Remind: the current implementation is static, as its assumed | |
| * its preferable to minimize creation of PrintService instances. | |
| * Later we should add logic to add/remove services on the fly which | |
| * will take a hit of needing to regather the list of services. | |
| */ | |
| private String defaultPrinter; | |
| private PrintService defaultPrintService; | |
| private PrintService[] printServices; /* includes the default printer */ | |
| private Vector<BackgroundLookupListener> lookupListeners = null; | |
| private static String debugPrefix = "PrintServiceLookupProvider>> "; | |
| private static boolean pollServices = true; | |
| private static final int DEFAULT_MINREFRESH = 120; // 2 minutes | |
| private static int minRefreshTime = DEFAULT_MINREFRESH; | |
| // List of commands used to deal with the printer queues on AIX | |
| String[] lpNameComAix = { | |
| "/usr/bin/lsallq", | |
| "/usr/bin/lpstat -W -p|/usr/bin/expand|/usr/bin/cut -f1 -d' '", | |
| "/usr/bin/lpstat -W -d|/usr/bin/expand|/usr/bin/cut -f1 -d' '", | |
| "/usr/bin/lpstat -W -v" | |
| }; | |
| private static final int aix_lsallq = 0; | |
| private static final int aix_lpstat_p = 1; | |
| private static final int aix_lpstat_d = 2; | |
| private static final int aix_lpstat_v = 3; | |
| private static int aix_defaultPrinterEnumeration = aix_lsallq; | |
| static { | |
| /* The system property "sun.java2d.print.polling" | |
| * can be used to force the printing code to poll or not poll | |
| * for PrintServices. | |
| */ | |
| String pollStr = System.getProperty("sun.java2d.print.polling"); | |
| if (pollStr != null) { | |
| if (pollStr.equalsIgnoreCase("true")) { | |
| pollServices = true; | |
| } else if (pollStr.equalsIgnoreCase("false")) { | |
| pollServices = false; | |
| } | |
| } | |
| /* The system property "sun.java2d.print.minRefreshTime" | |
| * can be used to specify minimum refresh time (in seconds) | |
| * for polling PrintServices. The default is 120. | |
| */ | |
| String refreshTimeStr = System.getProperty("sun.java2d.print.minRefreshTime"); | |
| if (refreshTimeStr != null) { | |
| try { | |
| minRefreshTime = Integer.parseInt(refreshTimeStr); | |
| } catch (NumberFormatException e) { | |
| } | |
| if (minRefreshTime < DEFAULT_MINREFRESH) { | |
| minRefreshTime = DEFAULT_MINREFRESH; | |
| } | |
| } | |
| /* The system property "sun.java2d.print.aix.lpstat" | |
| * can be used to force the usage of 'lpstat -p' to enumerate all | |
| * printer queues. By default we use 'lsallq', because 'lpstat -p' can | |
| * take lots of time if thousands of printers are attached to a server. | |
| */ | |
| if (isAIX()) { | |
| String aixPrinterEnumerator = System.getProperty("sun.java2d.print.aix.lpstat"); | |
| if (aixPrinterEnumerator != null) { | |
| if (aixPrinterEnumerator.equalsIgnoreCase("lpstat")) { | |
| aix_defaultPrinterEnumeration = aix_lpstat_p; | |
| } else if (aixPrinterEnumerator.equalsIgnoreCase("lsallq")) { | |
| aix_defaultPrinterEnumeration = aix_lsallq; | |
| } | |
| } | |
| } | |
| } | |
| static boolean isMac() { | |
| return OSInfo.getOSType() == OSInfo.OSType.MACOSX; | |
| } | |
| static boolean isLinux() { | |
| return OSInfo.getOSType() == OSInfo.OSType.LINUX; | |
| } | |
| static boolean isBSD() { | |
| return (OSInfo.getOSType() == OSInfo.OSType.LINUX || | |
| OSInfo.getOSType() == OSInfo.OSType.MACOSX); | |
| } | |
| static boolean isAIX() { | |
| return OSInfo.getOSType() == OSInfo.OSType.AIX; | |
| } | |
| static final int UNINITIALIZED = -1; | |
| static final int BSD_LPD = 0; | |
| static final int BSD_LPD_NG = 1; | |
| static int cmdIndex = UNINITIALIZED; | |
| String[] lpcFirstCom = { | |
| "/usr/sbin/lpc status | grep : | sed -ne '1,1 s/://p'", | |
| "/usr/sbin/lpc status | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'" | |
| }; | |
| String[] lpcAllCom = { | |
| "/usr/sbin/lpc status all | grep : | sed -e 's/://'", | |
| "/usr/sbin/lpc status all | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}' | sort" | |
| }; | |
| String[] lpcNameCom = { | |
| "| grep : | sed -ne 's/://p'", | |
| "| grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'" | |
| }; | |
| static int getBSDCommandIndex() { | |
| String command = "/usr/sbin/lpc status all"; | |
| String[] names = execCmd(command); | |
| if ((names == null) || (names.length == 0)) { | |
| return BSD_LPD_NG; | |
| } | |
| for (int i=0; i<names.length; i++) { | |
| if (names[i].indexOf('@') != -1) { | |
| return BSD_LPD_NG; | |
| } | |
| } | |
| return BSD_LPD; | |
| } | |
| public PrintServiceLookupProvider() { | |
| // start the printer listener thread | |
| if (pollServices) { | |
| Thread thr = new Thread(ThreadGroupUtils.getRootThreadGroup(), | |
| new PrinterChangeListener(), | |
| "PrinterListener", 0, false); | |
| thr.setContextClassLoader(null); | |
| thr.setDaemon(true); | |
| thr.start(); | |
| IPPPrintService.debug_println(debugPrefix+"polling turned on"); | |
| } | |
| } | |
| /* Want the PrintService which is default print service to have | |
| * equality of reference with the equivalent in list of print services | |
| * This isn't required by the API and there's a risk doing this will | |
| * lead people to assume its guaranteed. | |
| */ | |
| @Override | |
| public synchronized PrintService[] getPrintServices() { | |
| if (printServices == null || !pollServices) { | |
| refreshServices(); | |
| } | |
| if (printServices == null) { | |
| return new PrintService[0]; | |
| } else { | |
| return printServices.clone(); | |
| } | |
| } | |
| private int addPrintServiceToList(ArrayList<PrintService> printerList, PrintService ps) { | |
| int index = printerList.indexOf(ps); | |
| // Check if PrintService with same name is already in the list. | |
| if (CUPSPrinter.isCupsRunning() && index != -1) { | |
| // Bug in Linux: Duplicate entry of a remote printer | |
| // and treats it as local printer but it is returning wrong | |
| // information when queried using IPP. Workaround is to remove it. | |
| // Even CUPS ignores these entries as shown in lpstat or using | |
| // their web configuration. | |
| PrinterURI uri = ps.getAttribute(PrinterURI.class); | |
| if (uri.getURI().getHost().equals("localhost")) { | |
| IPPPrintService.debug_println(debugPrefix+"duplicate PrintService, ignoring the new local printer: "+ps); | |
| return index; // Do not add this. | |
| } | |
| PrintService oldPS = printerList.get(index); | |
| uri = oldPS.getAttribute(PrinterURI.class); | |
| if (uri.getURI().getHost().equals("localhost")) { | |
| IPPPrintService.debug_println(debugPrefix+"duplicate PrintService, removing existing local printer: "+oldPS); | |
| printerList.remove(oldPS); | |
| } else { | |
| return index; | |
| } | |
| } | |
| printerList.add(ps); | |
| return (printerList.size() - 1); | |
| } | |
| // refreshes "printServices" | |
| public synchronized void refreshServices() { | |
| /* excludes the default printer */ | |
| String[] printers = null; // array of printer names | |
| String[] printerURIs = null; //array of printer URIs | |
| try { | |
| getDefaultPrintService(); | |
| } catch (Throwable t) { | |
| IPPPrintService.debug_println(debugPrefix+ | |
| "Exception getting default printer : " + t); | |
| } | |
| if (CUPSPrinter.isCupsRunning()) { | |
| try { | |
| printerURIs = CUPSPrinter.getAllPrinters(); | |
| IPPPrintService.debug_println("CUPS URIs = " + printerURIs); | |
| if (printerURIs != null) { | |
| for (int p = 0; p < printerURIs.length; p++) { | |
| IPPPrintService.debug_println("URI="+printerURIs[p]); | |
| } | |
| } | |
| } catch (Throwable t) { | |
| IPPPrintService.debug_println(debugPrefix+ | |
| "Exception getting all CUPS printers : " + t); | |
| } | |
| if ((printerURIs != null) && (printerURIs.length > 0)) { | |
| printers = new String[printerURIs.length]; | |
| for (int i=0; i<printerURIs.length; i++) { | |
| int lastIndex = printerURIs[i].lastIndexOf("/"); | |
| printers[i] = printerURIs[i].substring(lastIndex+1); | |
| } | |
| } | |
| } else { | |
| if (isMac()) { | |
| printers = getAllPrinterNamesSysV(); | |
| } else if (isAIX()) { | |
| printers = getAllPrinterNamesAIX(); | |
| } else { //BSD | |
| printers = getAllPrinterNamesBSD(); | |
| } | |
| } | |
| if (printers == null) { | |
| if (defaultPrintService != null) { | |
| printServices = new PrintService[1]; | |
| printServices[0] = defaultPrintService; | |
| } else { | |
| printServices = null; | |
| } | |
| return; | |
| } | |
| ArrayList<PrintService> printerList = new ArrayList<>(); | |
| int defaultIndex = -1; | |
| for (int p=0; p<printers.length; p++) { | |
| if (printers[p] == null) { | |
| continue; | |
| } | |
| if ((defaultPrintService != null) | |
| && printers[p].equals(getPrinterDestName(defaultPrintService))) { | |
| defaultIndex = addPrintServiceToList(printerList, defaultPrintService); | |
| } else { | |
| if (printServices == null) { | |
| IPPPrintService.debug_println(debugPrefix+ | |
| "total# of printers = "+printers.length); | |
| if (CUPSPrinter.isCupsRunning()) { | |
| try { | |
| addPrintServiceToList(printerList, | |
| new IPPPrintService(printers[p], | |
| printerURIs[p], | |
| true)); | |
| } catch (Exception e) { | |
| IPPPrintService.debug_println(debugPrefix+ | |
| " getAllPrinters Exception "+ | |
| e); | |
| } | |
| } else { | |
| printerList.add(new UnixPrintService(printers[p])); | |
| } | |
| } else { | |
| int j; | |
| for (j=0; j<printServices.length; j++) { | |
| if (printServices[j] != null) { | |
| if (printers[p].equals(getPrinterDestName(printServices[j]))) { | |
| printerList.add(printServices[j]); | |
| printServices[j] = null; | |
| break; | |
| } | |
| } | |
| } | |
| if (j == printServices.length) { // not found? | |
| if (CUPSPrinter.isCupsRunning()) { | |
| try { | |
| addPrintServiceToList(printerList, | |
| new IPPPrintService(printers[p], | |
| printerURIs[p], | |
| true)); | |
| } catch (Exception e) { | |
| IPPPrintService.debug_println(debugPrefix+ | |
| " getAllPrinters Exception "+ | |
| e); | |
| } | |
| } else { | |
| printerList.add(new UnixPrintService(printers[p])); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Look for deleted services and invalidate these | |
| if (printServices != null) { | |
| for (int j=0; j < printServices.length; j++) { | |
| if ((printServices[j] instanceof UnixPrintService) && | |
| (!printServices[j].equals(defaultPrintService))) { | |
| ((UnixPrintService)printServices[j]).invalidateService(); | |
| } | |
| } | |
| } | |
| //if defaultService is not found in printerList | |
| if (defaultIndex == -1 && defaultPrintService != null) { | |
| defaultIndex = addPrintServiceToList(printerList, defaultPrintService); | |
| } | |
| printServices = printerList.toArray(new PrintService[] {}); | |
| // swap default with the first in the list | |
| if (defaultIndex > 0) { | |
| PrintService saveService = printServices[0]; | |
| printServices[0] = printServices[defaultIndex]; | |
| printServices[defaultIndex] = saveService; | |
| } | |
| } | |
| private boolean matchesAttributes(PrintService service, | |
| PrintServiceAttributeSet attributes) { | |
| Attribute [] attrs = attributes.toArray(); | |
| for (int i=0; i<attrs.length; i++) { | |
| @SuppressWarnings("unchecked") | |
| Attribute serviceAttr | |
| = service.getAttribute((Class<PrintServiceAttribute>)attrs[i].getCategory()); | |
| if (serviceAttr == null || !serviceAttr.equals(attrs[i])) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| /* This checks for validity of the printer name before passing as | |
| * parameter to a shell command. | |
| */ | |
| private boolean checkPrinterName(String s) { | |
| char c; | |
| for (int i=0; i < s.length(); i++) { | |
| c = s.charAt(i); | |
| if (Character.isLetterOrDigit(c) || | |
| c == '-' || c == '_' || c == '.' || c == '/') { | |
| continue; | |
| } else { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| /* | |
| * Gets the printer name compatible with the list of printers returned by | |
| * the system when we query default or all the available printers. | |
| */ | |
| private String getPrinterDestName(PrintService ps) { | |
| if (isMac()) { | |
| return ((IPPPrintService)ps).getDest(); | |
| } | |
| return ps.getName(); | |
| } | |
| /* On a network with many (hundreds) of network printers, it | |
| * can save several seconds if you know all you want is a particular | |
| * printer, to ask for that printer rather than retrieving all printers. | |
| */ | |
| private PrintService getServiceByName(PrinterName nameAttr) { | |
| String name = nameAttr.getValue(); | |
| if (name == null || name.isEmpty() || !checkPrinterName(name)) { | |
| return null; | |
| } | |
| /* check if all printers are already available */ | |
| if (printServices != null) { | |
| for (PrintService printService : printServices) { | |
| PrinterName printerName = printService.getAttribute(PrinterName.class); | |
| if (printerName.getValue().equals(name)) { | |
| return printService; | |
| } | |
| } | |
| } | |
| /* take CUPS into account first */ | |
| if (CUPSPrinter.isCupsRunning()) { | |
| try { | |
| return new IPPPrintService(name, | |
| newURL("http://"+ | |
| CUPSPrinter.getServer()+":"+ | |
| CUPSPrinter.getPort()+"/"+ | |
| name)); | |
| } catch (Exception e) { | |
| IPPPrintService.debug_println(debugPrefix+ | |
| " getServiceByName Exception "+ | |
| e); | |
| } | |
| } | |
| /* fallback if nothing not having a printer at this point */ | |
| PrintService printer = null; | |
| if (isMac()) { | |
| printer = getNamedPrinterNameSysV(name); | |
| } else if (isAIX()) { | |
| printer = getNamedPrinterNameAIX(name); | |
| } else { | |
| printer = getNamedPrinterNameBSD(name); | |
| } | |
| return printer; | |
| } | |
| private PrintService[] | |
| getPrintServices(PrintServiceAttributeSet serviceSet) { | |
| if (serviceSet == null || serviceSet.isEmpty()) { | |
| return getPrintServices(); | |
| } | |
| /* Typically expect that if a service attribute is specified that | |
| * its a printer name and there ought to be only one match. | |
| * Directly retrieve that service and confirm | |
| * that it meets the other requirements. | |
| * If printer name isn't mentioned then go a slow path checking | |
| * all printers if they meet the requirements. | |
| */ | |
| PrintService[] services; | |
| PrinterName name = (PrinterName)serviceSet.get(PrinterName.class); | |
| PrintService defService; | |
| if (name != null && (defService = getDefaultPrintService()) != null) { | |
| /* To avoid executing a unix command, see if the client is asking | |
| * for the default printer by name, since we already have that | |
| * initialised. | |
| */ | |
| PrinterName defName = defService.getAttribute(PrinterName.class); | |
| if (defName != null && name.equals(defName)) { | |
| if (matchesAttributes(defService, serviceSet)) { | |
| services = new PrintService[1]; | |
| services[0] = defService; | |
| return services; | |
| } else { | |
| return new PrintService[0]; | |
| } | |
| } else { | |
| /* Its not the default service */ | |
| PrintService service = getServiceByName(name); | |
| if (service != null && | |
| matchesAttributes(service, serviceSet)) { | |
| services = new PrintService[1]; | |
| services[0] = service; | |
| return services; | |
| } else { | |
| return new PrintService[0]; | |
| } | |
| } | |
| } else { | |
| /* specified service attributes don't include a name.*/ | |
| ArrayList<PrintService> matchedServices = new ArrayList<>(); | |
| services = getPrintServices(); | |
| for (int i = 0; i< services.length; i++) { | |
| if (matchesAttributes(services[i], serviceSet)) { | |
| matchedServices.add(services[i]); | |
| } | |
| } | |
| services = new PrintService[matchedServices.size()]; | |
| for (int i = 0; i< services.length; i++) { | |
| services[i] = matchedServices.get(i); | |
| } | |
| return services; | |
| } | |
| } | |
| /* | |
| * If service attributes are specified then there must be additional | |
| * filtering. | |
| */ | |
| @Override | |
| public PrintService[] getPrintServices(DocFlavor flavor, | |
| AttributeSet attributes) { | |
| PrintRequestAttributeSet requestSet = null; | |
| PrintServiceAttributeSet serviceSet = null; | |
| if (attributes != null && !attributes.isEmpty()) { | |
| requestSet = new HashPrintRequestAttributeSet(); | |
| serviceSet = new HashPrintServiceAttributeSet(); | |
| Attribute[] attrs = attributes.toArray(); | |
| for (int i=0; i<attrs.length; i++) { | |
| if (attrs[i] instanceof PrintRequestAttribute) { | |
| requestSet.add(attrs[i]); | |
| } else if (attrs[i] instanceof PrintServiceAttribute) { | |
| serviceSet.add(attrs[i]); | |
| } | |
| } | |
| } | |
| PrintService[] services = getPrintServices(serviceSet); | |
| if (services.length == 0) { | |
| return services; | |
| } | |
| if (CUPSPrinter.isCupsRunning()) { | |
| ArrayList<PrintService> matchingServices = new ArrayList<>(); | |
| for (int i=0; i<services.length; i++) { | |
| try { | |
| if (services[i]. | |
| getUnsupportedAttributes(flavor, requestSet) == null) { | |
| matchingServices.add(services[i]); | |
| } | |
| } catch (IllegalArgumentException e) { | |
| } | |
| } | |
| services = new PrintService[matchingServices.size()]; | |
| return matchingServices.toArray(services); | |
| } else { | |
| // We only need to compare 1 PrintService because all | |
| // UnixPrintServices are the same anyway. We will not use | |
| // default PrintService because it might be null. | |
| PrintService service = services[0]; | |
| if ((flavor == null || | |
| service.isDocFlavorSupported(flavor)) && | |
| service.getUnsupportedAttributes(flavor, requestSet) == null) | |
| { | |
| return services; | |
| } else { | |
| return new PrintService[0]; | |
| } | |
| } | |
| } | |
| /* | |
| * return empty array as don't support multi docs | |
| */ | |
| @Override | |
| public MultiDocPrintService[] | |
| getMultiDocPrintServices(DocFlavor[] flavors, | |
| AttributeSet attributes) { | |
| return new MultiDocPrintService[0]; | |
| } | |
| @Override | |
| public synchronized PrintService getDefaultPrintService() { | |
| // clear defaultPrintService | |
| defaultPrintService = null; | |
| String psuri = null; | |
| IPPPrintService.debug_println("isRunning ? "+ | |
| (CUPSPrinter.isCupsRunning())); | |
| if (CUPSPrinter.isCupsRunning()) { | |
| String[] printerInfo = CUPSPrinter.getDefaultPrinter(); | |
| if (printerInfo != null && printerInfo.length >= 2) { | |
| defaultPrinter = printerInfo[0]; | |
| psuri = printerInfo[1]; | |
| } | |
| } else { | |
| if (isMac()) { | |
| defaultPrinter = getDefaultPrinterNameSysV(); | |
| } else if (isAIX()) { | |
| defaultPrinter = getDefaultPrinterNameAIX(); | |
| } else { | |
| defaultPrinter = getDefaultPrinterNameBSD(); | |
| } | |
| } | |
| if (defaultPrinter == null) { | |
| return null; | |
| } | |
| defaultPrintService = null; | |
| if (printServices != null) { | |
| for (int j=0; j<printServices.length; j++) { | |
| if (defaultPrinter.equals(getPrinterDestName(printServices[j]))) { | |
| defaultPrintService = printServices[j]; | |
| break; | |
| } | |
| } | |
| } | |
| if (defaultPrintService == null) { | |
| if (CUPSPrinter.isCupsRunning()) { | |
| try { | |
| PrintService defaultPS; | |
| if ((psuri != null) && !psuri.startsWith("file")) { | |
| defaultPS = new IPPPrintService(defaultPrinter, | |
| psuri, true); | |
| } else { | |
| defaultPS = new IPPPrintService(defaultPrinter, | |
| newURL("http://"+ | |
| CUPSPrinter.getServer()+":"+ | |
| CUPSPrinter.getPort()+"/"+ | |
| defaultPrinter)); | |
| } | |
| defaultPrintService = defaultPS; | |
| } catch (Exception e) { | |
| } | |
| } else { | |
| defaultPrintService = new UnixPrintService(defaultPrinter); | |
| } | |
| } | |
| return defaultPrintService; | |
| } | |
| @Override | |
| public synchronized void | |
| getServicesInbackground(BackgroundLookupListener listener) { | |
| if (printServices != null) { | |
| listener.notifyServices(printServices); | |
| } else { | |
| if (lookupListeners == null) { | |
| lookupListeners = new Vector<>(); | |
| lookupListeners.add(listener); | |
| Thread lookupThread = new Thread(this); | |
| lookupThread.start(); | |
| } else { | |
| lookupListeners.add(listener); | |
| } | |
| } | |
| } | |
| /* This method isn't used in most cases because we rely on code in | |
| * javax.print.PrintServiceLookup. This is needed just for the cases | |
| * where those interfaces are by-passed. | |
| */ | |
| private PrintService[] copyOf(PrintService[] inArr) { | |
| if (inArr == null || inArr.length == 0) { | |
| return inArr; | |
| } else { | |
| PrintService []outArr = new PrintService[inArr.length]; | |
| System.arraycopy(inArr, 0, outArr, 0, inArr.length); | |
| return outArr; | |
| } | |
| } | |
| @Override | |
| public void run() { | |
| PrintService[] services = getPrintServices(); | |
| synchronized (this) { | |
| BackgroundLookupListener listener; | |
| for (int i=0; i<lookupListeners.size(); i++) { | |
| listener = lookupListeners.elementAt(i); | |
| listener.notifyServices(copyOf(services)); | |
| } | |
| lookupListeners = null; | |
| } | |
| } | |
| private String getDefaultPrinterNameBSD() { | |
| if (cmdIndex == UNINITIALIZED) { | |
| cmdIndex = getBSDCommandIndex(); | |
| } | |
| String[] names = execCmd(lpcFirstCom[cmdIndex]); | |
| if (names == null || names.length == 0) { | |
| return null; | |
| } | |
| if ((cmdIndex==BSD_LPD_NG) && | |
| (names[0].startsWith("missingprinter"))) { | |
| return null; | |
| } | |
| return names[0]; | |
| } | |
| private PrintService getNamedPrinterNameBSD(String name) { | |
| if (cmdIndex == UNINITIALIZED) { | |
| cmdIndex = getBSDCommandIndex(); | |
| } | |
| String command = "/usr/sbin/lpc status " + name + lpcNameCom[cmdIndex]; | |
| String[] result = execCmd(command); | |
| if (result == null || !(result[0].equals(name))) { | |
| return null; | |
| } | |
| return new UnixPrintService(name); | |
| } | |
| private String[] getAllPrinterNamesBSD() { | |
| if (cmdIndex == UNINITIALIZED) { | |
| cmdIndex = getBSDCommandIndex(); | |
| } | |
| String[] names = execCmd(lpcAllCom[cmdIndex]); | |
| if (names == null || names.length == 0) { | |
| return null; | |
| } | |
| return names; | |
| } | |
| static String getDefaultPrinterNameSysV() { | |
| String defaultPrinter = "lp"; | |
| String command = "/usr/bin/lpstat -d"; | |
| String [] names = execCmd(command); | |
| if (names == null || names.length == 0) { | |
| return defaultPrinter; | |
| } else { | |
| int index = names[0].indexOf(":"); | |
| if (index == -1 || (names[0].length() <= index+1)) { | |
| return null; | |
| } else { | |
| String name = names[0].substring(index+1).trim(); | |
| if (name.length() == 0) { | |
| return null; | |
| } else { | |
| return name; | |
| } | |
| } | |
| } | |
| } | |
| private PrintService getNamedPrinterNameSysV(String name) { | |
| String command = "/usr/bin/lpstat -v " + name; | |
| String []result = execCmd(command); | |
| if (result == null || result[0].indexOf("unknown printer") > 0) { | |
| return null; | |
| } else { | |
| return new UnixPrintService(name); | |
| } | |
| } | |
| private String[] getAllPrinterNamesSysV() { | |
| String defaultPrinter = "lp"; | |
| String command = "/usr/bin/lpstat -v|/usr/bin/expand|/usr/bin/cut -f3 -d' ' |/usr/bin/cut -f1 -d':' | /usr/bin/sort"; | |
| String [] names = execCmd(command); | |
| ArrayList<String> printerNames = new ArrayList<>(); | |
| for (int i=0; i < names.length; i++) { | |
| if (!names[i].equals("_default") && | |
| !names[i].equals(defaultPrinter) && | |
| !names[i].isEmpty()) { | |
| printerNames.add(names[i]); | |
| } | |
| } | |
| return printerNames.toArray(new String[printerNames.size()]); | |
| } | |
| private String getDefaultPrinterNameAIX() { | |
| String[] names = execCmd(lpNameComAix[aix_lpstat_d]); | |
| // Remove headers and bogus entries added by remote printers. | |
| names = UnixPrintService.filterPrinterNamesAIX(names); | |
| if (names == null || names.length != 1) { | |
| // No default printer found | |
| return null; | |
| } else { | |
| return names[0]; | |
| } | |
| } | |
| private PrintService getNamedPrinterNameAIX(String name) { | |
| // On AIX there should be no blank after '-v'. | |
| String[] result = execCmd(lpNameComAix[aix_lpstat_v] + name); | |
| // Remove headers and bogus entries added by remote printers. | |
| result = UnixPrintService.filterPrinterNamesAIX(result); | |
| if (result == null || result.length != 1) { | |
| return null; | |
| } else { | |
| return new UnixPrintService(name); | |
| } | |
| } | |
| private String[] getAllPrinterNamesAIX() { | |
| // Determine all printers of the system. | |
| String [] names = execCmd(lpNameComAix[aix_defaultPrinterEnumeration]); | |
| // Remove headers and bogus entries added by remote printers. | |
| names = UnixPrintService.filterPrinterNamesAIX(names); | |
| ArrayList<String> printerNames = new ArrayList<String>(); | |
| for ( int i=0; i < names.length; i++) { | |
| printerNames.add(names[i]); | |
| } | |
| return printerNames.toArray(new String[printerNames.size()]); | |
| } | |
| static String[] execCmd(final String command) { | |
| ArrayList<String> results = null; | |
| try { | |
| final String[] cmd = new String[3]; | |
| if (isAIX()) { | |
| cmd[0] = "/usr/bin/sh"; | |
| cmd[1] = "-c"; | |
| cmd[2] = "env LC_ALL=C " + command; | |
| } else { | |
| cmd[0] = "/bin/sh"; | |
| cmd[1] = "-c"; | |
| cmd[2] = "LC_ALL=C " + command; | |
| } | |
| Process proc; | |
| BufferedReader bufferedReader = null; | |
| File f = Files.createTempFile("prn", "xc") | |
| .toFile(); | |
| cmd[2] = cmd[2] + ">" + f.getAbsolutePath(); | |
| proc = Runtime.getRuntime().exec(cmd); | |
| try { | |
| boolean done = false; // in case of interrupt. | |
| while (!done) { | |
| try { | |
| proc.waitFor(); | |
| done = true; | |
| } catch (InterruptedException ignored) { | |
| } | |
| } | |
| if (proc.exitValue() == 0) { | |
| FileReader reader = new FileReader(f); | |
| bufferedReader = new BufferedReader(reader); | |
| String line; | |
| results = new ArrayList<>(); | |
| while ((line = bufferedReader.readLine()) | |
| != null) { | |
| results.add(line); | |
| } | |
| } | |
| } catch (Exception e) { | |
| // Print exception for tracking printer command errors | |
| IPPPrintService.debug_println("Printer command error: " + e); | |
| } finally { | |
| f.delete(); | |
| // promptly close all streams. | |
| if (bufferedReader != null) { | |
| bufferedReader.close(); | |
| } | |
| proc.getInputStream() | |
| .close(); | |
| proc.getErrorStream() | |
| .close(); | |
| proc.getOutputStream() | |
| .close(); | |
| } | |
| } catch (IOException ignored) { | |
| } | |
| if (results == null) { | |
| return new String[0]; | |
| } else { | |
| return results.toArray(new String[results.size()]); | |
| } | |
| } | |
| private final class PrinterChangeListener implements Runnable { | |
| @Override | |
| public void run() { | |
| int refreshSecs; | |
| while (true) { | |
| try { | |
| refreshServices(); | |
| } catch (Exception se) { | |
| IPPPrintService.debug_println(debugPrefix+"Exception in refresh thread."); | |
| break; | |
| } | |
| if ((printServices != null) && | |
| (printServices.length > minRefreshTime)) { | |
| // compute new refresh time 1 printer = 1 sec | |
| refreshSecs = printServices.length; | |
| } else { | |
| refreshSecs = minRefreshTime; | |
| } | |
| try { | |
| Thread.sleep(refreshSecs * 1000); | |
| } catch (InterruptedException e) { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| @SuppressWarnings("deprecation") | |
| private static URL newURL(String spec) throws MalformedURLException { | |
| return new URL(spec); | |
| } | |
| } |