Skip to content

Commit a85dc55

Browse files
committed
8263311: Watch registry changes for remote printers update instead of polling
Reviewed-by: psadhukhan, serb
1 parent 3f31a6b commit a85dc55

File tree

3 files changed

+49
-176
lines changed

3 files changed

+49
-176
lines changed

src/java.desktop/windows/classes/sun/print/PrintServiceLookupProvider.java

Lines changed: 9 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,8 +26,6 @@
2626
package sun.print;
2727

2828
import java.util.ArrayList;
29-
import java.util.Arrays;
30-
import java.util.Comparator;
3129

3230
import javax.print.DocFlavor;
3331
import javax.print.MultiDocPrintService;
@@ -50,31 +48,7 @@ public class PrintServiceLookupProvider extends PrintServiceLookup {
5048
private String[] printers; /* excludes the default printer */
5149
private PrintService[] printServices; /* includes the default printer */
5250

53-
private static final int DEFAULT_REFRESH_TIME = 240; // 4 minutes
54-
private static final int MINIMUM_REFRESH_TIME = 120; // 2 minutes
55-
private static final boolean pollServices;
56-
private static final int refreshTime;
57-
5851
static {
59-
/* The system property "sun.java2d.print.polling"
60-
* can be used to force the printing code to poll or not poll
61-
* for PrintServices.
62-
*/
63-
String pollStr = java.security.AccessController.doPrivileged(
64-
new sun.security.action.GetPropertyAction("sun.java2d.print.polling"));
65-
pollServices = !("false".equalsIgnoreCase(pollStr));
66-
67-
/* The system property "sun.java2d.print.minRefreshTime"
68-
* can be used to specify minimum refresh time (in seconds)
69-
* for polling PrintServices. The default is 240.
70-
*/
71-
String refreshTimeStr = java.security.AccessController.doPrivileged(
72-
new sun.security.action.GetPropertyAction(
73-
"sun.java2d.print.minRefreshTime"));
74-
refreshTime = (refreshTimeStr != null)
75-
? getRefreshTime(refreshTimeStr)
76-
: DEFAULT_REFRESH_TIME;
77-
7852
java.security.AccessController.doPrivileged(
7953
new java.security.PrivilegedAction<Void>() {
8054
public Void run() {
@@ -84,17 +58,6 @@ public Void run() {
8458
});
8559
}
8660

87-
private static int getRefreshTime(final String refreshTimeStr) {
88-
try {
89-
int minRefreshTime = Integer.parseInt(refreshTimeStr);
90-
return (minRefreshTime < MINIMUM_REFRESH_TIME)
91-
? MINIMUM_REFRESH_TIME
92-
: minRefreshTime;
93-
} catch (NumberFormatException e) {
94-
return DEFAULT_REFRESH_TIME;
95-
}
96-
}
97-
9861
/* The singleton win32 print lookup service.
9962
* Code that is aware of this field and wants to use it must first
10063
* see if its null, and if so instantiate it by calling a method such as
@@ -126,13 +89,11 @@ public PrintServiceLookupProvider() {
12689
thr.setDaemon(true);
12790
thr.start();
12891

129-
if (pollServices) {
130-
// start the remote printer listener thread
131-
Thread remThr = new Thread(null, new RemotePrinterChangeListener(),
132-
"RemotePrinterListener", 0, false);
133-
remThr.setDaemon(true);
134-
remThr.start();
135-
}
92+
// start the remote printer listener thread
93+
Thread remThr = new Thread(null, new RemotePrinterChangeListener(),
94+
"RemotePrinterListener", 0, false);
95+
remThr.setDaemon(true);
96+
remThr.start();
13697
} /* else condition ought to never happen! */
13798
}
13899

@@ -356,70 +317,15 @@ public void run() {
356317
}
357318
}
358319

359-
/* Windows provides *PrinterChangeNotification* functions that provides
360-
information about printer status changes of the local printers but not
361-
network printers.
362-
Alternatively, Windows provides a way through which one can get the
363-
network printer status changes by using WMI, RegistryKeyChange combination,
364-
which is a slightly complex mechanism.
365-
The Windows WMI offers an async and sync method to read through registry
366-
via the WQL query. The async method is considered dangerous as it leaves
367-
open a channel until we close it. But the async method has the advantage of
368-
being notified of a change in registry by calling callback without polling for it.
369-
The sync method uses the polling mechanism to notify.
370-
RegistryValueChange cannot be used in combination with WMI to get registry
371-
value change notification because of an error that may be generated because the
372-
scope of the query would be too big to handle(at times).
373-
Hence an alternative mechanism is chosen via the EnumPrinters by polling for the
374-
count of printer status changes(add\remove) and based on it update the printers
375-
list.
376-
*/
377-
class RemotePrinterChangeListener implements Comparator<String>, Runnable {
378-
379-
RemotePrinterChangeListener() {
380-
}
381-
382-
@Override
383-
public int compare(String o1, String o2) {
384-
return ((o1 == null)
385-
? ((o2 == null) ? 0 : 1)
386-
: ((o2 == null) ? -1 : o1.compareTo(o2)));
387-
}
388-
320+
private final class RemotePrinterChangeListener implements Runnable {
389321
@Override
390322
public void run() {
391-
// Init the list of remote printers
392-
String[] prevRemotePrinters = getRemotePrintersNames();
393-
if (prevRemotePrinters != null) {
394-
Arrays.sort(prevRemotePrinters, this);
395-
}
396-
397-
while (true) {
398-
try {
399-
Thread.sleep(refreshTime * 1000);
400-
} catch (InterruptedException e) {
401-
break;
402-
}
403-
404-
String[] currentRemotePrinters = getRemotePrintersNames();
405-
if (currentRemotePrinters != null) {
406-
Arrays.sort(currentRemotePrinters, this);
407-
}
408-
if (!Arrays.equals(prevRemotePrinters, currentRemotePrinters)) {
409-
// The list of remote printers got updated,
410-
// so update the cached list printers which
411-
// includes both local and network printers
412-
refreshServices();
413-
414-
// store the current data for next comparison
415-
prevRemotePrinters = currentRemotePrinters;
416-
}
417-
}
323+
notifyRemotePrinterChange(); // busy loop in the native code
418324
}
419325
}
420326

421327
private native String getDefaultPrinterName();
422328
private native String[] getAllPrinterNames();
423329
private native void notifyLocalPrinterChange();
424-
private native String[] getRemotePrintersNames();
330+
private native void notifyRemotePrinterChange();
425331
}

src/java.desktop/windows/native/libawt/windows/WPrinterJob.cpp

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,6 @@ Java_sun_print_PrintServiceLookupProvider_getAllPrinterNames(JNIEnv *env,
191191
return getPrinterNames(env, PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS);
192192
}
193193

194-
JNIEXPORT jobjectArray JNICALL
195-
Java_sun_print_PrintServiceLookupProvider_getRemotePrintersNames(JNIEnv *env,
196-
jobject peer)
197-
{
198-
return getPrinterNames(env, PRINTER_ENUM_CONNECTIONS);
199-
}
200194

201195
JNIEXPORT void JNICALL
202196
Java_sun_print_PrintServiceLookupProvider_notifyLocalPrinterChange(JNIEnv *env,
@@ -239,6 +233,37 @@ Java_sun_print_PrintServiceLookupProvider_notifyLocalPrinterChange(JNIEnv *env,
239233
::ClosePrinter(hPrinter);
240234
}
241235

236+
JNIEXPORT void JNICALL
237+
Java_sun_print_PrintServiceLookupProvider_notifyRemotePrinterChange(JNIEnv *env,
238+
jobject peer)
239+
{
240+
jclass cls = env->GetObjectClass(peer);
241+
CHECK_NULL(cls);
242+
jmethodID refresh = env->GetMethodID(cls, "refreshServices", "()V");
243+
CHECK_NULL(refresh);
244+
245+
HKEY hKey;
246+
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER,
247+
_T("Printers\\Connections"),
248+
0, KEY_NOTIFY, &hKey)) {
249+
return;
250+
}
251+
252+
BOOL keepMonitoring;
253+
do {
254+
keepMonitoring =
255+
ERROR_SUCCESS == RegNotifyChangeKeyValue(hKey, TRUE,
256+
REG_NOTIFY_CHANGE_NAME,
257+
NULL,
258+
FALSE);
259+
if (keepMonitoring) {
260+
env->CallVoidMethod(peer, refresh);
261+
}
262+
} while (keepMonitoring && !env->ExceptionCheck());
263+
264+
RegCloseKey(hKey);
265+
}
266+
242267

243268
JNIEXPORT jfloatArray JNICALL
244269
Java_sun_print_Win32PrintService_getMediaPrintableArea(JNIEnv *env,

test/jdk/java/awt/print/RemotePrinterStatusRefresh/RemotePrinterStatusRefresh.java

Lines changed: 9 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,10 +23,10 @@
2323

2424
/*
2525
* @test
26-
* @bug 8153732 8212202 8221263 8221412 8222108
26+
* @bug 8153732 8212202 8221263 8221412 8222108 8263311
2727
* @requires (os.family == "Windows")
2828
* @summary Windows remote printer changes do not reflect in lookupPrintServices()
29-
* @run main/manual/othervm -Dsun.java2d.print.minRefreshTime=120 RemotePrinterStatusRefresh
29+
* @run main/manual RemotePrinterStatusRefresh
3030
*/
3131

3232
import java.awt.BorderLayout;
@@ -63,12 +63,7 @@
6363

6464
public class RemotePrinterStatusRefresh extends WindowAdapter {
6565

66-
private static final long DEFAULT_REFRESH_TIME = 240L;
67-
private static final long MINIMAL_REFRESH_TIME = 120L;
68-
69-
private static final long refreshTime = getRefreshTime();
70-
71-
private static final long TIMEOUT = refreshTime * 4 + 60;
66+
private static final long TIMEOUT = 15L * 60;
7267

7368

7469
private static final CountDownLatch latch = new CountDownLatch(1);
@@ -86,7 +81,6 @@ public class RemotePrinterStatusRefresh extends WindowAdapter {
8681
private final ServiceItemListModel beforeList;
8782
private final ServiceItemListModel afterList;
8883

89-
private JTextField nextRefresh;
9084
private JTextField timeLeft;
9185

9286
private final Timer timer;
@@ -184,22 +178,18 @@ public Component getListCellRendererComponent(JList<?> list,
184178
+ "configured printers.\n"
185179
+ "Step 1: Add or Remove a network printer using "
186180
+ "Windows Control Panel.\n"
187-
+ "Step 2: Wait for 2\u20134 minutes after adding or removing.\n"
188-
+ " \"Next printer refresh in\" gives you a "
189-
+ "rough estimation on when update will happen.\n"
190-
+ "Step 3: Click Refresh."
181+
+ "Step 2: Click Refresh."
191182
+ "\"After\" list is populated with updated list "
192183
+ "of printers.\n"
193-
+ "Step 4: Compare the list of printers in \"Before\" and "
184+
+ "Step 3: Compare the list of printers in \"Before\" and "
194185
+ "\"After\" lists.\n"
195186
+ " Added printers are highlighted with "
196187
+ "green color, removed ones \u2014 with "
197188
+ "red color.\n"
198-
+ "Step 5: Click Pass if the list of printers is correctly "
189+
+ "Step 4: Click Pass if the list of printers is correctly "
199190
+ "updated.\n"
200-
+ "Step 6: If the list is not updated, wait for another "
201-
+ "2\u20134 minutes, and then click Refresh again.\n"
202-
+ "Step 7: If the list does not update, click Fail.\n"
191+
+ "Step 5: If the list is not updated, click Refresh again.\n"
192+
+ "Step 6: If the list does not update, click Fail.\n"
203193
+ "\n"
204194
+ "You have to click Refresh to enable Pass and Fail buttons. "
205195
+ "If no button is pressed,\n"
@@ -216,18 +206,6 @@ public static void main(String[] args) throws Exception {
216206
}
217207
}
218208

219-
private static long getRefreshTime() {
220-
String refreshTime =
221-
System.getProperty("sun.java2d.print.minRefreshTime",
222-
Long.toString(DEFAULT_REFRESH_TIME));
223-
try {
224-
long value = Long.parseLong(refreshTime);
225-
return value < MINIMAL_REFRESH_TIME ? MINIMAL_REFRESH_TIME : value;
226-
} catch (NumberFormatException e) {
227-
return DEFAULT_REFRESH_TIME;
228-
}
229-
}
230-
231209
private static void createUI() {
232210
test = new RemotePrinterStatusRefresh();
233211
}
@@ -278,31 +256,6 @@ private JPanel createInfoPanel() {
278256
javaVersion.setEditable(false);
279257
javaLabel.setLabelFor(javaVersion);
280258

281-
JLabel refreshTimeLabel = new JLabel("Refresh interval:");
282-
long minutes = refreshTime / 60;
283-
long seconds = refreshTime % 60;
284-
String interval = String.format("%1$d seconds%2$s",
285-
refreshTime,
286-
minutes > 0
287-
? String.format(" (%1$d %2$s%3$s)",
288-
minutes,
289-
minutes > 1 ? "minutes" : "minute",
290-
seconds > 0
291-
? String.format(" %1$d %2$s",
292-
seconds,
293-
seconds > 1 ? "seconds" : "second")
294-
: "")
295-
: ""
296-
);
297-
JTextField refreshInterval = new JTextField(interval);
298-
refreshInterval.setEditable(false);
299-
refreshTimeLabel.setLabelFor(refreshInterval);
300-
301-
JLabel nextRefreshLabel = new JLabel("Next printer refresh in:");
302-
nextRefresh = new JTextField();
303-
nextRefresh.setEditable(false);
304-
nextRefreshLabel.setLabelFor(nextRefresh);
305-
306259
JLabel timeoutLabel = new JLabel("Time left:");
307260
timeLeft = new JTextField();
308261
timeLeft.setEditable(false);
@@ -317,14 +270,10 @@ private JPanel createInfoPanel() {
317270
layout.createSequentialGroup()
318271
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
319272
.addComponent(javaLabel)
320-
.addComponent(refreshTimeLabel)
321-
.addComponent(nextRefreshLabel)
322273
.addComponent(timeoutLabel)
323274
)
324275
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING, true)
325276
.addComponent(javaVersion)
326-
.addComponent(refreshInterval)
327-
.addComponent(nextRefresh)
328277
.addComponent(timeLeft)
329278
)
330279
);
@@ -334,12 +283,6 @@ private JPanel createInfoPanel() {
334283
.addComponent(javaLabel)
335284
.addComponent(javaVersion)
336285
)
337-
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
338-
.addComponent(refreshTimeLabel)
339-
.addComponent(refreshInterval))
340-
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
341-
.addComponent(nextRefreshLabel)
342-
.addComponent(nextRefresh))
343286
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
344287
.addComponent(timeoutLabel)
345288
.addComponent(timeLeft))
@@ -493,7 +436,6 @@ private void updateTimeLeft(ActionEvent e) {
493436
disposeUI();
494437
}
495438
timeLeft.setText(formatTime(left));
496-
nextRefresh.setText(formatTime(refreshTime - (elapsed % refreshTime)));
497439
}
498440

499441
private static String formatTime(final long seconds) {

0 commit comments

Comments
 (0)