提交 2ef257ad 编写于 作者: S sveerabhadra

8153732: Windows remote printer changes do not reflect in lookupPrintServices()

Reviewed-by: prr, psadhukhan
上级 242cf2e0
...@@ -53,8 +53,42 @@ public class PrintServiceLookupProvider extends PrintServiceLookup { ...@@ -53,8 +53,42 @@ public class PrintServiceLookupProvider extends PrintServiceLookup {
private PrintService defaultPrintService; private PrintService defaultPrintService;
private String[] printers; /* excludes the default printer */ private String[] printers; /* excludes the default printer */
private PrintService[] printServices; /* includes the default printer */ private PrintService[] printServices; /* includes the default printer */
private static boolean pollServices = true;
private static final int DEFAULT_MINREFRESH = 240; // 4 minutes
private static int minRefreshTime = DEFAULT_MINREFRESH;
static { 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 = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("sun.java2d.print.polling"));
if (pollStr != null) {
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 240.
*/
String refreshTimeStr = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"sun.java2d.print.minRefreshTime"));
if (refreshTimeStr != null) {
try {
minRefreshTime = (Integer.valueOf(refreshTimeStr)).intValue();
} catch (NumberFormatException e) {
}
if (minRefreshTime < DEFAULT_MINREFRESH) {
minRefreshTime = DEFAULT_MINREFRESH;
}
}
java.security.AccessController.doPrivileged( java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() { new java.security.PrivilegedAction<Void>() {
public Void run() { public Void run() {
...@@ -96,10 +130,17 @@ public class PrintServiceLookupProvider extends PrintServiceLookup { ...@@ -96,10 +130,17 @@ public class PrintServiceLookupProvider extends PrintServiceLookup {
if (osName != null && osName.startsWith("Windows 98")) { if (osName != null && osName.startsWith("Windows 98")) {
return; return;
} }
// start the printer listener thread // start the local printer listener thread
PrinterChangeListener thr = new PrinterChangeListener(); Thread thr = new PrinterChangeListener();
thr.setDaemon(true); thr.setDaemon(true);
thr.start(); thr.start();
if (pollServices) {
// start the remote printer listener thread
Thread remThr = new RemotePrinterChangeListener();
remThr.setDaemon(true);
remThr.start();
}
} /* else condition ought to never happen! */ } /* else condition ought to never happen! */
} }
...@@ -340,9 +381,74 @@ public class PrintServiceLookupProvider extends PrintServiceLookup { ...@@ -340,9 +381,74 @@ public class PrintServiceLookupProvider extends PrintServiceLookup {
} }
} }
/* Windows provides *PrinterChangeNotification* functions that provides
information about printer status changes of the local printers but not
network printers.
Alternatively, Windows provides a way thro' which one can get the
network printer status changes by using WMI, RegistryKeyChange combination,
which is a slightly complex mechanism.
The Windows WMI offers an async and sync method to read thro' registry
via the WQL query. The async method is considered dangerous as it leaves
open a channel until we close it. But the async method has the advantage of
being notified of a change in registry by calling callback without polling for it.
The sync method uses the polling mechanism to notify.
RegistryValueChange cannot be used in combination with WMI to get registry
value change notification because of an error that may be generated because the
scope of the query would be too big to handle(at times).
Hence an alternative mechanism is choosen via the EnumPrinters by polling for the
count of printer status changes(add\remove) and based on it update the printers
list.
*/
class RemotePrinterChangeListener extends Thread {
private String[] prevRemotePrinters;
RemotePrinterChangeListener() {
prevRemotePrinters = getRemotePrintersNames();
}
boolean doCompare(String[] str1, String[] str2) {
if (str1.length != str2.length) {
return true;
} else {
for (int i = 0;i < str1.length;i++) {
for (int j = 0;j < str2.length;j++) {
if (!str1[i].equals(str2[j])) {
return true;
}
}
}
}
return false;
}
@Override
public void run() {
while (true) {
String[] currentRemotePrinters = getRemotePrintersNames();
if (doCompare(prevRemotePrinters, currentRemotePrinters)) {
// updated the printers data
// printers list now contains both local and network printer data
refreshServices();
// store the current data for next comparison
prevRemotePrinters = currentRemotePrinters;
}
try {
Thread.sleep(minRefreshTime * 1000);
} catch (InterruptedException e) {
break;
}
}
}
}
private native String getDefaultPrinterName(); private native String getDefaultPrinterName();
private native String[] getAllPrinterNames(); private native String[] getAllPrinterNames();
private native long notifyFirstPrinterChange(String printer); private native long notifyFirstPrinterChange(String printer);
private native void notifyClosePrinterChange(long chgObj); private native void notifyClosePrinterChange(long chgObj);
private native int notifyPrinterChange(long chgObj); private native int notifyPrinterChange(long chgObj);
private native String[] getRemotePrintersNames();
} }
...@@ -232,6 +232,76 @@ Java_sun_print_PrintServiceLookupProvider_notifyPrinterChange(JNIEnv *env, ...@@ -232,6 +232,76 @@ Java_sun_print_PrintServiceLookupProvider_notifyPrinterChange(JNIEnv *env,
} }
} }
JNIEXPORT jobjectArray JNICALL
Java_sun_print_PrintServiceLookupProvider_getRemotePrintersNames(JNIEnv *env,
jobject peer)
{
TRY;
int remotePrintersCount = 0;
DWORD cbNeeded = 0;
DWORD cReturned = 0;
LPBYTE pPrinterEnum = NULL;
LPBYTE pNetworkPrinterLoc = NULL;
jstring utf_str;
jclass clazz = env->FindClass("java/lang/String");
if (clazz == NULL) {
return NULL;
}
jobjectArray nameArray;
try {
::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
NULL, 4, NULL, 0, &cbNeeded, &cReturned);
pPrinterEnum = new BYTE[cbNeeded];
pNetworkPrinterLoc = new BYTE[cbNeeded/sizeof(PRINTER_INFO_4)];
::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
NULL, 4, pPrinterEnum, cbNeeded, &cbNeeded,
&cReturned);
if (cReturned > 0) {
for (DWORD i = 0; i < cReturned; i++) {
PRINTER_INFO_4 *info4 = (PRINTER_INFO_4 *) (pPrinterEnum + i * sizeof(PRINTER_INFO_4));
// Store the network printers indexes
if (info4->Attributes & PRINTER_ATTRIBUTE_NETWORK) {
pNetworkPrinterLoc[remotePrintersCount++] = i;
}
}
// Allocate space only for the network type printers
nameArray = env->NewObjectArray(remotePrintersCount, clazz, NULL);
if (nameArray == NULL) {
throw std::bad_alloc();
}
} else {
nameArray = NULL;
}
// Loop thro' network printers list only
for (int i = 0; i < remotePrintersCount; i++) {
PRINTER_INFO_4 *info4 = (PRINTER_INFO_4 *)
(pPrinterEnum + pNetworkPrinterLoc[i] * sizeof(PRINTER_INFO_4));
utf_str = JNU_NewStringPlatform(env, info4->pPrinterName);
if (utf_str == NULL) {
throw std::bad_alloc();
}
env->SetObjectArrayElement(nameArray, i, utf_str);
env->DeleteLocalRef(utf_str);
}
} catch (std::bad_alloc&) {
delete [] pPrinterEnum;
delete [] pNetworkPrinterLoc;
throw;
}
delete [] pPrinterEnum;
delete [] pNetworkPrinterLoc;
return nameArray;
CATCH_BAD_ALLOC_RET(NULL);
}
JNIEXPORT jfloatArray JNICALL JNIEXPORT jfloatArray JNICALL
Java_sun_print_Win32PrintService_getMediaPrintableArea(JNIEnv *env, Java_sun_print_Win32PrintService_getMediaPrintableArea(JNIEnv *env,
......
/*
* Copyright (c) 2018, 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.
*
* 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.
*/
/**
* @test
* @bug 8153732
* @requires (os.family == "Windows")
* @summary Windows remote printer changes do not reflect in lookupPrintServices()
* @ignore Requires a new network printer installation\removal
* @run main/manual RemotePrinterStatusRefresh
*/
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import java.awt.print.PrinterJob;
import javax.print.PrintService;
public class RemotePrinterStatusRefresh
{
private static TestUI test = null;
public static void main(String args[]) throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
// Test UI creation
test = new TestUI(latch);
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
try {
test.createUI();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
// RemotePrinterStatusRefresh creation
RemotePrinterStatusRefresh RemotePrinterStatusRefresh = new RemotePrinterStatusRefresh();
SwingUtilities.invokeAndWait(() -> {
collectPrintersList(test.resultsTextArea, true);
});
// 8 min = 480000 msec
if(waitForFlag(480000)) {
SwingUtilities.invokeAndWait(() -> {
collectPrintersList(test.resultsTextArea, false);
});
} else {
dispose();
throw new RuntimeException("No new network printer got added/removed!! Test timed out!!");
}
boolean status = latch.await(1, TimeUnit.MINUTES);
if (!status) {
dispose();
throw new RuntimeException("Test timed out.");
}
if (test.testResult == false) {
dispose();
throw new RuntimeException("Test Failed.");
}
dispose();
}
public static void dispose() throws Exception {
SwingUtilities.invokeAndWait(() -> {
test.disposeUI();
});
}
public static boolean waitForFlag (long maxTimeoutInMsec) throws Exception {
while(!test.isAdded && maxTimeoutInMsec > 0) {
maxTimeoutInMsec -= 100;
Thread.sleep(100);
}
if(maxTimeoutInMsec <= 0) {
return false;
} else {
return true;
}
}
private static void collectPrintersList(JTextArea textArea, boolean before) {
if(before) {
System.out.println("List of printers(before): ");
textArea.setText("List of printers(before): \n");
for (PrintService printServiceBefore : PrinterJob.lookupPrintServices()) {
System.out.println(printServiceBefore);
textArea.append(printServiceBefore.toString());
textArea.append("\n");
}
} else {
textArea.append("\n");
System.out.println("List of printers(after): ");
textArea.append("List of printers(after): \n");
for (PrintService printServiceAfter : PrinterJob.lookupPrintServices()) {
System.out.println(printServiceAfter);
textArea.append(printServiceAfter.toString());
textArea.append("\n");
}
}
}
}
class TestUI {
private static JFrame mainFrame;
private static JPanel mainControlPanel;
private static JTextArea instructionTextArea;
private static JPanel resultButtonPanel;
private static JButton passButton;
private static JButton failButton;
private static JButton addedButton;
private static JPanel testPanel;
private static JButton testButton;
private static JLabel buttonPressCountLabel;
private static GridBagLayout layout;
private final CountDownLatch latch;
public boolean testResult = false;
public volatile Boolean isAdded = false;
public static JTextArea resultsTextArea;
public TestUI(CountDownLatch latch) throws Exception {
this.latch = latch;
}
public final void createUI() {
mainFrame = new JFrame("RemotePrinterStatusRefresh");
layout = new GridBagLayout();
mainControlPanel = new JPanel(layout);
resultButtonPanel = new JPanel(layout);
testPanel = new JPanel(layout);
GridBagConstraints gbc = new GridBagConstraints();
// Create Test instructions
String instructions
= "This test displays the current list of printers(before) attached to \n"
+ "this computer in the results panel.\n\n"
+ "Please follow the below steps for this manual test\n"
+ "--------------------------------------------------------------------\n"
+ "Step 1: Add/Remove a new network printer and Wait for 4 minutes after adding/removing\n"
+ "Step 2: Then click on 'Printer Added/Removed' button\n"
+ "Step 2: Once the new network printer is added/removed, see if it is \n"
+ " the same as displayed/not displayed in the results panel.\n"
+ "Step 3: If displayed/not displayed, then click 'Pass' else click on 'Fail' button";
instructionTextArea = new JTextArea();
instructionTextArea.setText(instructions);
instructionTextArea.setEditable(false);
instructionTextArea.setBorder(BorderFactory.
createTitledBorder("Test Instructions"));
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
mainControlPanel.add(instructionTextArea, gbc);
gbc.gridx = 0;
gbc.gridy = 1;
testPanel.add(Box.createVerticalStrut(50));
mainControlPanel.add(testPanel);
addedButton = new JButton("Printer Added/Removed");
addedButton.setActionCommand("Added");
addedButton.addActionListener((ActionEvent e) -> {
System.out.println("Added Button pressed!");
isAdded = true;
});
// Create resultButtonPanel with Pass, Fail buttons
passButton = new JButton("Pass");
passButton.setActionCommand("Pass");
passButton.addActionListener((ActionEvent e) -> {
System.out.println("Pass Button pressed!");
testResult = true;
latch.countDown();
disposeUI();
});
failButton = new JButton("Fail");
failButton.setActionCommand("Fail");
failButton.addActionListener((ActionEvent e) -> {
System.out.println("Fail Button pressed!");
testResult = false;
latch.countDown();
disposeUI();
});
gbc.gridx = 0;
gbc.gridy = 0;
resultButtonPanel.add(addedButton, gbc);
gbc.gridx = 1;
gbc.gridy = 0;
resultButtonPanel.add(passButton, gbc);
gbc.gridx = 2;
gbc.gridy = 0;
resultButtonPanel.add(failButton, gbc);
resultsTextArea = new JTextArea();
resultsTextArea.setEditable(false);
resultsTextArea.setBorder(BorderFactory.
createTitledBorder("Results"));
gbc.gridx = 0;
gbc.gridy = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
mainControlPanel.add(resultsTextArea, gbc);
gbc.gridx = 0;
gbc.gridy = 2;
mainControlPanel.add(resultButtonPanel, gbc);
mainFrame.add(mainControlPanel);
mainFrame.pack();
mainFrame.setVisible(true);
}
public void disposeUI() {
mainFrame.dispose();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册