blob: ddeea1ad30f83dfa0f6c85127524f6fafc268d33 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.print;
27
28import java.io.BufferedReader;
29import java.io.FileInputStream;
30import java.io.InputStream;
31import java.io.InputStreamReader;
32import java.io.IOException;
33import java.util.ArrayList;
34import java.util.Vector;
35import java.security.AccessController;
36import java.security.PrivilegedActionException;
37import java.security.PrivilegedExceptionAction;
38import javax.print.DocFlavor;
39import javax.print.MultiDocPrintService;
40import javax.print.PrintService;
41import javax.print.PrintServiceLookup;
42import javax.print.attribute.Attribute;
43import javax.print.attribute.AttributeSet;
44import javax.print.attribute.HashPrintRequestAttributeSet;
45import javax.print.attribute.HashPrintServiceAttributeSet;
46import javax.print.attribute.PrintRequestAttribute;
47import javax.print.attribute.PrintRequestAttributeSet;
48import javax.print.attribute.PrintServiceAttribute;
49import javax.print.attribute.PrintServiceAttributeSet;
50import javax.print.attribute.standard.PrinterName;
51import java.io.File;
52import java.io.FileReader;
53import java.net.URL;
54
55/*
56 * Remind: This class uses solaris commands. We also need a linux
57 * version
58 */
59public class UnixPrintServiceLookup extends PrintServiceLookup
60 implements BackgroundServiceLookup, Runnable {
61
62 /* Remind: the current implementation is static, as its assumed
63 * its preferable to minimise creation of PrintService instances.
64 * Later we should add logic to add/remove services on the fly which
65 * will take a hit of needing to regather the list of services.
66 */
67 private String defaultPrinter;
68 private PrintService defaultPrintService;
69 private PrintService[] printServices; /* includes the default printer */
70 private Vector lookupListeners = null;
71 private static String debugPrefix = "UnixPrintServiceLookup>> ";
72 private static boolean pollServices = true;
73 private static final int DEFAULT_MINREFRESH = 120; // 2 minutes
74 private static int minRefreshTime = DEFAULT_MINREFRESH;
75
76
77 static String osname;
78
79 static {
80 /* The system property "sun.java2d.print.polling"
81 * can be used to force the printing code to poll or not poll
82 * for PrintServices.
83 */
84 String pollStr = java.security.AccessController.doPrivileged(
85 new sun.security.action.GetPropertyAction("sun.java2d.print.polling"));
86
87 if (pollStr != null) {
88 if (pollStr.equalsIgnoreCase("true")) {
89 pollServices = true;
90 } else if (pollStr.equalsIgnoreCase("false")) {
91 pollServices = false;
92 }
93 }
94
95 /* The system property "sun.java2d.print.minRefreshTime"
96 * can be used to specify minimum refresh time (in seconds)
97 * for polling PrintServices. The default is 120.
98 */
99 String refreshTimeStr = java.security.AccessController.doPrivileged(
100 new sun.security.action.GetPropertyAction(
101 "sun.java2d.print.minRefreshTime"));
102
103 if (refreshTimeStr != null) {
104 try {
105 minRefreshTime = (new Integer(refreshTimeStr)).intValue();
106 } catch (NumberFormatException e) {
107 }
108 if (minRefreshTime < DEFAULT_MINREFRESH) {
109 minRefreshTime = DEFAULT_MINREFRESH;
110 }
111 }
112
113 osname = java.security.AccessController.doPrivileged(
114 new sun.security.action.GetPropertyAction("os.name"));
115 }
116
117 static boolean isSysV() {
118 return osname.equals("SunOS");
119 }
120
121 static boolean isBSD() {
122 return osname.equals("Linux");
123 }
124
125 static final int UNINITIALIZED = -1;
126 static final int BSD_LPD = 0;
127 static final int BSD_LPD_NG = 1;
128
129 static int cmdIndex = UNINITIALIZED;
130
131 String[] lpcFirstCom = {
132 "/usr/sbin/lpc status | grep : | sed -ne '1,1 s/://p'",
133 "/usr/sbin/lpc status | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'"
134 };
135
136 String[] lpcAllCom = {
137 "/usr/sbin/lpc status | grep : | sed -e 's/://'",
138 "/usr/sbin/lpc -a status | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}' | sort"
139 };
140
141 String[] lpcNameCom = {
142 "| grep : | sed -ne 's/://p'",
143 "| grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'"
144 };
145
146
147 static int getBSDCommandIndex() {
148 String command = "/usr/sbin/lpc status";
149 String[] names = execCmd(command);
150
151 if ((names == null) || (names.length == 0)) {
152 return BSD_LPD_NG;
153 }
154
155 for (int i=0; i<names.length; i++) {
156 if (names[i].indexOf('@') != -1) {
157 return BSD_LPD_NG;
158 }
159 }
160
161 return BSD_LPD;
162 }
163
164
165 public UnixPrintServiceLookup() {
166 // start the printer listener thread
167 if (pollServices) {
168 PrinterChangeListener thr = new PrinterChangeListener();
169 thr.setDaemon(true);
170 thr.start();
171 IPPPrintService.debug_println(debugPrefix+"polling turned on");
172 }
173 }
174
175 /* Want the PrintService which is default print service to have
176 * equality of reference with the equivalent in list of print services
177 * This isn't required by the API and there's a risk doing this will
178 * lead people to assume its guaranteed.
179 */
180 public synchronized PrintService[] getPrintServices() {
181 SecurityManager security = System.getSecurityManager();
182 if (security != null) {
183 security.checkPrintJobAccess();
184 }
185
186 if (printServices == null || !pollServices) {
187 refreshServices();
188 }
189 if (printServices == null) {
190 return new PrintService[0];
191 } else {
192 return printServices;
193 }
194 }
195
196
197 // refreshes "printServices"
198 public synchronized void refreshServices() {
199 String[] printers; /* excludes the default printer */
200
201 getDefaultPrintService();
202 if (CUPSPrinter.isCupsRunning()) {
203 printers = CUPSPrinter.getAllPrinters();
204 } else {
205 if (isSysV()) {
206 printers = getAllPrinterNamesSysV();
207 } else { //BSD
208 printers = getAllPrinterNamesBSD();
209 }
210 }
211
212 if (printers == null) {
213 if (defaultPrintService != null) {
214 printServices = new PrintService[1];
215 printServices[0] = defaultPrintService;
216 } else {
217 printServices = null;
218 }
219 return;
220 }
221
222 ArrayList printerList = new ArrayList();
223 int defaultIndex = -1;
224 for (int p=0; p<printers.length; p++) {
225 if (printers[p] == null) {
226 continue;
227 }
228 if ((defaultPrintService != null)
229 && printers[p].equals(defaultPrintService.getName())) {
230 printerList.add(defaultPrintService);
231 defaultIndex = printerList.size() - 1;
232 } else {
233 if (printServices == null) {
234 IPPPrintService.debug_println(debugPrefix+
235 "total# of printers = "+printers.length);
236
237 if (CUPSPrinter.isCupsRunning()) {
238 try {
239 URL serviceURL =
240 new URL("http://"+
241 CUPSPrinter.getServer()+":"+
242 CUPSPrinter.getPort()+"/"+printers[p]);
243 printerList.add(new IPPPrintService( printers[p],
244 serviceURL));
245 } catch (Exception e) {
246 IPPPrintService.debug_println(debugPrefix+
247 " getAllPrinters Exception "+
248 e);
249
250 }
251 } else {
252 printerList.add(new UnixPrintService(printers[p]));
253 }
254 } else {
255 int j;
256 for (j=0; j<printServices.length; j++) {
257 if ((printServices[j] != null) &&
258 (printers[p].equals(printServices[j].getName()))) {
259 printerList.add(printServices[j]);
260 printServices[j] = null;
261 break;
262 }
263 }
264
265 if (j == printServices.length) { // not found?
266 if (CUPSPrinter.isCupsRunning()) {
267 try {
268 URL serviceURL =
269 new URL("http://"+
270 CUPSPrinter.getServer()+":"+
271 CUPSPrinter.getPort()+"/"+printers[p]);
272 printerList.add(new IPPPrintService( printers[p],
273 serviceURL));
274 } catch (Exception e) {
275 IPPPrintService.debug_println(debugPrefix+
276 " getAllPrinters Exception "+
277 e);
278
279 }
280 } else {
281 printerList.add(new UnixPrintService(printers[p]));
282 }
283 }
284 }
285 }
286 }
287
288 // Look for deleted services and invalidate these
289 if (printServices != null) {
290 for (int j=0; j < printServices.length; j++) {
291 if ((printServices[j] instanceof UnixPrintService) &&
292 (!printServices[j].equals(defaultPrintService))) {
293 ((UnixPrintService)printServices[j]).invalidateService();
294 }
295 }
296 }
297
298 //if defaultService is not found in printerList
299 if (defaultIndex == -1 && defaultPrintService != null) {
300 //add default to the list
301 printerList.add(defaultPrintService);
302 defaultIndex = printerList.size() - 1;
303 }
304
305 printServices = (PrintService[])printerList.toArray(
306 new PrintService[] {});
307
308 // swap default with the first in the list
309 if (defaultIndex > 0) {
310 PrintService saveService = printServices[0];
311 printServices[0] = printServices[defaultIndex];
312 printServices[defaultIndex] = saveService;
313 }
314 }
315
316 private boolean matchesAttributes(PrintService service,
317 PrintServiceAttributeSet attributes) {
318
319 Attribute [] attrs = attributes.toArray();
320 Attribute serviceAttr;
321 for (int i=0; i<attrs.length; i++) {
322 serviceAttr
323 = service.getAttribute((Class<PrintServiceAttribute>)attrs[i].getCategory());
324 if (serviceAttr == null || !serviceAttr.equals(attrs[i])) {
325 return false;
326 }
327 }
328 return true;
329 }
330
331 /* This checks for validity of the printer name before passing as
332 * parameter to a shell command.
333 */
334 private boolean checkPrinterName(String s) {
335 char c;
336
337 for (int i=0; i < s.length(); i++) {
338 c = s.charAt(i);
339 if (Character.isLetterOrDigit(c) ||
340 c == '-' || c == '_' || c == '.' || c == '/') {
341 continue;
342 } else {
343 return false;
344 }
345 }
346 return true;
347 }
348
349 /* On a network with many (hundreds) of network printers, it
350 * can save several seconds if you know all you want is a particular
351 * printer, to ask for that printer rather than retrieving all printers.
352 */
353 private PrintService getServiceByName(PrinterName nameAttr) {
354 String name = nameAttr.getValue();
355 PrintService printer = null;
356 if (name == null || name.equals("") || !checkPrinterName(name)) {
357 return null;
358 }
359 if (isSysV()) {
360 printer = getNamedPrinterNameSysV(name);
361 } else {
362 printer = getNamedPrinterNameBSD(name);
363 }
364 return printer;
365 }
366
367 private PrintService[]
368 getPrintServices(PrintServiceAttributeSet serviceSet) {
369
370 if (serviceSet == null || serviceSet.isEmpty()) {
371 return getPrintServices();
372 }
373
374 /* Typically expect that if a service attribute is specified that
375 * its a printer name and there ought to be only one match.
376 * Directly retrieve that service and confirm
377 * that it meets the other requirements.
378 * If printer name isn't mentioned then go a slow path checking
379 * all printers if they meet the reqiremements.
380 */
381 PrintService[] services;
382 PrinterName name = (PrinterName)serviceSet.get(PrinterName.class);
383 PrintService defService;
384 if (name != null && (defService = getDefaultPrintService()) != null) {
385 /* To avoid execing a unix command see if the client is asking
386 * for the default printer by name, since we already have that
387 * initialised.
388 */
389
390 PrinterName defName =
391 (PrinterName)defService.getAttribute(PrinterName.class);
392
393 if (defName != null && name.equals(defName)) {
394 if (matchesAttributes(defService, serviceSet)) {
395 services = new PrintService[1];
396 services[0] = defService;
397 return services;
398 } else {
399 return new PrintService[0];
400 }
401 } else {
402 /* Its not the default service */
403 PrintService service = getServiceByName(name);
404 if (service != null &&
405 matchesAttributes(service, serviceSet)) {
406 services = new PrintService[1];
407 services[0] = service;
408 return services;
409 } else {
410 return new PrintService[0];
411 }
412 }
413 } else {
414 /* specified service attributes don't include a name.*/
415 Vector matchedServices = new Vector();
416 services = getPrintServices();
417 for (int i = 0; i< services.length; i++) {
418 if (matchesAttributes(services[i], serviceSet)) {
419 matchedServices.add(services[i]);
420 }
421 }
422 services = new PrintService[matchedServices.size()];
423 for (int i = 0; i< services.length; i++) {
424 services[i] = (PrintService)matchedServices.elementAt(i);
425 }
426 return services;
427 }
428 }
429
430 /*
431 * If service attributes are specified then there must be additional
432 * filtering.
433 */
434 public PrintService[] getPrintServices(DocFlavor flavor,
435 AttributeSet attributes) {
436 SecurityManager security = System.getSecurityManager();
437 if (security != null) {
438 security.checkPrintJobAccess();
439 }
440 PrintRequestAttributeSet requestSet = null;
441 PrintServiceAttributeSet serviceSet = null;
442
443 if (attributes != null && !attributes.isEmpty()) {
444
445 requestSet = new HashPrintRequestAttributeSet();
446 serviceSet = new HashPrintServiceAttributeSet();
447
448 Attribute[] attrs = attributes.toArray();
449 for (int i=0; i<attrs.length; i++) {
450 if (attrs[i] instanceof PrintRequestAttribute) {
451 requestSet.add(attrs[i]);
452 } else if (attrs[i] instanceof PrintServiceAttribute) {
453 serviceSet.add(attrs[i]);
454 }
455 }
456 }
457
458 PrintService[] services = getPrintServices(serviceSet);
459 if (services.length == 0) {
460 return services;
461 }
462
463 if (CUPSPrinter.isCupsRunning()) {
464 ArrayList matchingServices = new ArrayList();
465 for (int i=0; i<services.length; i++) {
466 try {
467 if (services[i].
468 getUnsupportedAttributes(flavor, requestSet) == null) {
469 matchingServices.add(services[i]);
470 }
471 } catch (IllegalArgumentException e) {
472 }
473 }
474 services = new PrintService[matchingServices.size()];
475 return (PrintService[])matchingServices.toArray(services);
476
477 } else {
478 // We only need to compare 1 PrintService because all
479 // UnixPrintServices are the same anyway. We will not use
480 // default PrintService because it might be null.
481 PrintService service = services[0];
482 if ((flavor == null ||
483 service.isDocFlavorSupported(flavor)) &&
484 service.getUnsupportedAttributes(flavor, requestSet) == null)
485 {
486 return services;
487 } else {
488 return new PrintService[0];
489 }
490 }
491 }
492
493 /*
494 * return empty array as don't support multi docs
495 */
496 public MultiDocPrintService[]
497 getMultiDocPrintServices(DocFlavor[] flavors,
498 AttributeSet attributes) {
499 SecurityManager security = System.getSecurityManager();
500 if (security != null) {
501 security.checkPrintJobAccess();
502 }
503 return new MultiDocPrintService[0];
504 }
505
506
507 public synchronized PrintService getDefaultPrintService() {
508 SecurityManager security = System.getSecurityManager();
509 if (security != null) {
510 security.checkPrintJobAccess();
511 }
512
513 // clear defaultPrintService
514 defaultPrintService = null;
515
516 IPPPrintService.debug_println("isRunning ? "+
517 (CUPSPrinter.isCupsRunning()));
518 if (CUPSPrinter.isCupsRunning()) {
519 defaultPrinter = CUPSPrinter.getDefaultPrinter();
520 } else {
521 if (isSysV()) {
522 defaultPrinter = getDefaultPrinterNameSysV();
523 } else {
524 defaultPrinter = getDefaultPrinterNameBSD();
525 }
526 }
527 if (defaultPrinter == null) {
528 return null;
529 }
530 defaultPrintService = null;
531 if (printServices != null) {
532 for (int j=0; j<printServices.length; j++) {
533 if (defaultPrinter.equals(printServices[j].getName())) {
534 defaultPrintService = printServices[j];
535 break;
536 }
537 }
538 }
539 if (defaultPrintService == null) {
540 if (CUPSPrinter.isCupsRunning()) {
541 try {
542 PrintService defaultPS =
543 new IPPPrintService(defaultPrinter,
544 new URL("http://"+
545 CUPSPrinter.getServer()+":"+
546 CUPSPrinter.getPort()+"/"+
547 defaultPrinter));
548 defaultPrintService = defaultPS;
549 } catch (Exception e) {
550 }
551 } else {
552 defaultPrintService = new UnixPrintService(defaultPrinter);
553 }
554 }
555
556 return defaultPrintService;
557 }
558
559 public synchronized void
560 getServicesInbackground(BackgroundLookupListener listener) {
561 if (printServices != null) {
562 listener.notifyServices(printServices);
563 } else {
564 if (lookupListeners == null) {
565 lookupListeners = new Vector();
566 lookupListeners.add(listener);
567 Thread lookupThread = new Thread(this);
568 lookupThread.start();
569 } else {
570 lookupListeners.add(listener);
571 }
572 }
573 }
574
575 /* This method isn't used in most cases because we rely on code in
576 * javax.print.PrintServiceLookup. This is needed just for the cases
577 * where those interfaces are by-passed.
578 */
579 private PrintService[] copyOf(PrintService[] inArr) {
580 if (inArr == null || inArr.length == 0) {
581 return inArr;
582 } else {
583 PrintService []outArr = new PrintService[inArr.length];
584 System.arraycopy(inArr, 0, outArr, 0, inArr.length);
585 return outArr;
586 }
587 }
588
589 public void run() {
590 PrintService[] services = getPrintServices();
591 synchronized (this) {
592 BackgroundLookupListener listener;
593 for (int i=0; i<lookupListeners.size(); i++) {
594 listener =
595 (BackgroundLookupListener)lookupListeners.elementAt(i);
596 listener.notifyServices(copyOf(services));
597 }
598 lookupListeners = null;
599 }
600 }
601
602 private String getDefaultPrinterNameBSD() {
603 if (cmdIndex == UNINITIALIZED) {
604 cmdIndex = getBSDCommandIndex();
605 }
606 String[] names = execCmd(lpcFirstCom[cmdIndex]);
607 if (names == null || names.length == 0) {
608 return null;
609 }
610
611 if ((cmdIndex==BSD_LPD_NG) &&
612 (names[0].startsWith("missingprinter"))) {
613 return null;
614 }
615 return names[0];
616 }
617
618 private PrintService getNamedPrinterNameBSD(String name) {
619 if (cmdIndex == UNINITIALIZED) {
620 cmdIndex = getBSDCommandIndex();
621 }
622 String command = "/usr/sbin/lpc status " + name + lpcNameCom[cmdIndex];
623 String[] result = execCmd(command);
624
625 if (result == null || !(result[0].equals(name))) {
626 return null;
627 }
628 return new UnixPrintService(name);
629 }
630
631 private String[] getAllPrinterNamesBSD() {
632 if (cmdIndex == UNINITIALIZED) {
633 cmdIndex = getBSDCommandIndex();
634 }
635 String[] names = execCmd(lpcAllCom[cmdIndex]);
636 if (names == null || names.length == 0) {
637 return null;
638 }
639 return names;
640 }
641
642 private String getDefaultPrinterNameSysV() {
643 String defaultPrinter = "lp";
644 String command = "/usr/bin/lpstat -d";
645
646 String [] names = execCmd(command);
647 if (names == null || names.length == 0) {
648 return defaultPrinter;
649 } else {
650 int index = names[0].indexOf(":");
651 if (index == -1 || (names[0].length() <= index+1)) {
652 return null;
653 } else {
654 String name = names[0].substring(index+1).trim();
655 if (name.length() == 0) {
656 return null;
657 } else {
658 return name;
659 }
660 }
661 }
662 }
663
664 private PrintService getNamedPrinterNameSysV(String name) {
665
666 String command = "/usr/bin/lpstat -v " + name;
667 String []result = execCmd(command);
668
669 if (result == null || result[0].indexOf("unknown printer") > 0) {
670 return null;
671 } else {
672 return new UnixPrintService(name);
673 }
674 }
675
676 private String[] getAllPrinterNamesSysV() {
677 String defaultPrinter = "lp";
678 String command = "/usr/bin/lpstat -v|/usr/bin/expand|/usr/bin/cut -f3 -d' ' |/usr/bin/cut -f1 -d':' | /usr/bin/sort";
679
680 String [] names = execCmd(command);
681 ArrayList printerNames = new ArrayList();
682 for (int i=0; i < names.length; i++) {
683 if (!names[i].equals("_default") &&
684 !names[i].equals(defaultPrinter) &&
685 !names[i].equals("")) {
686 printerNames.add(names[i]);
687 }
688 }
689 return (String[])printerNames.toArray(new String[printerNames.size()]);
690 }
691
692 static String[] execCmd(final String command) {
693 ArrayList results = null;
694 try {
695 final String[] cmd = new String[3];
696 if (isSysV()) {
697 cmd[0] = "/usr/bin/sh";
698 cmd[1] = "-c";
699 cmd[2] = "env LC_ALL=C " + command;
700 } else {
701 cmd[0] = "/bin/sh";
702 cmd[1] = "-c";
703 cmd[2] = "LC_ALL=C " + command;
704 }
705
706 results = (ArrayList)AccessController.doPrivileged(
707 new PrivilegedExceptionAction() {
708 public Object run() throws IOException {
709
710 Process proc;
711 BufferedReader bufferedReader = null;
712 File f = File.createTempFile("prn","xc");
713 cmd[2] = cmd[2]+">"+f.getAbsolutePath();
714
715 proc = Runtime.getRuntime().exec(cmd);
716 try {
717 boolean done = false; // in case of interrupt.
718 while (!done) {
719 try {
720 proc.waitFor();
721 done = true;
722 } catch (InterruptedException e) {
723 }
724 }
725
726 if (proc.exitValue() == 0) {
727 FileReader reader = new FileReader(f);
728 bufferedReader = new BufferedReader(reader);
729 String line;
730 ArrayList results = new ArrayList();
731 while ((line = bufferedReader.readLine())
732 != null) {
733 results.add(line);
734 }
735 return results;
736 }
737 } finally {
738 f.delete();
739 // promptly close all streams.
740 if (bufferedReader != null) {
741 bufferedReader.close();
742 }
743 proc.getInputStream().close();
744 proc.getErrorStream().close();
745 proc.getOutputStream().close();
746 }
747 return null;
748 }
749 });
750 } catch (PrivilegedActionException e) {
751 }
752 if (results == null) {
753 return new String[0];
754 } else {
755 return (String[])results.toArray(new String[results.size()]);
756 }
757 }
758
759 private class PrinterChangeListener extends Thread {
760
761 public void run() {
762 int refreshSecs;
763 while (true) {
764 try {
765 refreshServices();
766 } catch (Exception se) {
767 IPPPrintService.debug_println(debugPrefix+"Exception in refresh thread.");
768 break;
769 }
770
771 if ((printServices != null) &&
772 (printServices.length > minRefreshTime)) {
773 // compute new refresh time 1 printer = 1 sec
774 refreshSecs = printServices.length;
775 } else {
776 refreshSecs = minRefreshTime;
777 }
778 try {
779 sleep(refreshSecs * 1000);
780 } catch (InterruptedException e) {
781 break;
782 }
783 }
784 }
785 }
786}