source: main/trunk/gli/src/org/greenstone/gatherer/download/DownloadJob.java@ 31878

Last change on this file since 31878 was 31878, checked in by ak19, 7 years ago
  1. Previous commit message was incorrect: it wasn't that perl wasn't found that resulted in the open3() failure error message, but that something on the PATH wasn't available to it, possible wget itself. 2. Updating unused DownloadJob.old_callDownload() to have the recently committed changes in callDownload(). 3. Emacs tabbing for recently committed files.
  • Property svn:keywords set to Author Date Id Revision
File size: 49.0 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37package org.greenstone.gatherer.download;
38
39import java.awt.event.*;
40import java.io.*;
41import java.net.*;
42import java.util.*;
43import javax.swing.tree.*;
44import javax.swing.SwingUtilities;
45import org.greenstone.gatherer.Configuration;
46import org.greenstone.gatherer.DebugStream;
47import org.greenstone.gatherer.Dictionary;
48import org.greenstone.gatherer.Gatherer;
49import org.greenstone.gatherer.GAuthenticator;
50import org.greenstone.gatherer.greenstone.LocalGreenstone;
51import org.greenstone.gatherer.file.WorkspaceTree;
52import org.greenstone.gatherer.util.AppendLineOnlyFileDocument;
53import org.greenstone.gatherer.util.SafeProcess;
54import org.greenstone.gatherer.util.Utility;
55import org.greenstone.gatherer.cdm.Argument;
56import org.greenstone.gatherer.collection.*;
57
58/**
59 * @author John Thompson, Greenstone Digital Library, University of Waikato
60 * @version 2.0
61 * When modifying this class, bear in mind concurrency issues that could arise with
62 * SafeProcess's worker threads and where synchronization may be needed to prevent such issues.
63 */
64public class DownloadJob
65 implements ActionListener, SafeProcess.MainProcessHandler {
66
67 private AppendLineOnlyFileDocument download_log;
68
69 private DownloadProgressBar progress;
70
71 private int previous_state;
72 private int state;
73
74 private SafeProcess prcs = null;
75
76 private final String download_url;
77 private boolean wasClosed = false;
78
79 // private String current_url;
80 // private String destination;
81 private final String proxy_pass;
82 private final String proxy_user;
83
84 //private final Vector encountered_urls;
85 //private Vector failed_urls;
86 private Download download;
87 private DownloadScrollPane mummy;
88 private HashMap download_option;
89
90 public static final int COMPLETE = 0;
91 public static final int PAUSED = 1;
92 public static final int RUNNING = 2;
93 public static final int STOPPED = 3;
94
95 public static final int UNKNOWN_MAX = 0;
96 public static final int DEFINED_MAX = 1;
97 public static final int UNDEFINED_MAX = 2;
98
99 // To prematurely terminate wget, we will need to use sockets and find a free port.
100 // We will look at a limited range of ports. This range will be reused (circular buffer)
101 private static final int PORT_BASE = 50000;
102 private static final int PORT_BLOCK_SIZE = 100;
103 private static int nextFreePort = PORT_BASE; // Keep track what port numbers we have checked for availability
104 int port; // package access. The socket port number this instance of DownloadJob will use
105 // only the main thread (where DownloadJob runs) modifies port, so no synching needed
106
107 private final String mode;
108
109 private String proxy_url; // only the main thread (where DownloadJob runs) modifies this, so no synching needed
110
111 /**
112 */
113 public DownloadJob(Download download, String proxy_pass, String proxy_user, DownloadScrollPane mummy, String mode, String proxy_url) {
114 URL url = null;
115 int folder_hash;
116
117 this.proxy_url = proxy_url;
118
119 download_option = downloadToHashMap(download);
120 if (!mode.equals("Z3950") && !mode.equals("SRW")) {
121 Argument url_arg = (Argument)download_option.get((String)"url");
122 download_url = url_arg.getValue();
123
124 }
125 else {
126 Argument host_arg = (Argument)download_option.get((String)"host");
127 Argument port_arg = (Argument)download_option.get((String)"port");
128 download_url = host_arg.getValue() + ":" +port_arg.getValue();
129 }
130
131 folder_hash = download_url.hashCode();
132 String log_filename = Utility.getLogDir(null) + "download-"+ mode + folder_hash + ".log";
133 File log_file = new File(log_filename);
134 if(log_file.exists()) {
135 log_file.delete();
136 }
137
138 File parent_log_file = log_file.getParentFile();
139 parent_log_file.mkdirs();
140 parent_log_file = null;
141 log_file = null;
142
143 this.download_log = new AppendLineOnlyFileDocument(log_filename, false);
144
145 this.proxy_pass = proxy_pass;
146 this.proxy_user = proxy_user;
147 this.mummy = mummy;
148 this.mode = mode;
149 this.download = download;
150
151 progress = new DownloadProgressBar(this,download_url, true);
152 //encountered_urls = new Vector();
153 //failed_urls = new Vector();
154
155 previous_state = STOPPED;
156 state = STOPPED;
157 }
158
159 private HashMap downloadToHashMap(Download download)
160 {
161 HashMap download_option = new HashMap();
162 ArrayList arguments = download.getArguments(true, false);
163 for(int i = 0; i < arguments.size(); i++) {
164 Argument argument = (Argument) arguments.get(i);
165 download_option.put(argument.getName(), argument);
166 }
167 return download_option;
168 }
169
170 /** Depending on which button on the progress bar was pushed,
171 * this method will affect the state of the DownloadJob and perhaps make
172 * calls to wget.class if necessary.
173 * @param event The ActionEvent fired from within the DownloadProgressBar
174 * which we must respond to.
175 */
176 public void old_actionPerformed(ActionEvent event) {
177 // The stop_start_button is used to alternately start or stop the
178 // job. If the current state of the job is paused then this
179 // restart is logically equivalent to a resume.
180 if(event.getSource() == progress.stop_start_button) {
181 previous_state = state;
182 if (state == RUNNING) {
183 state = STOPPED;
184 } else {
185 //previous_state = state;
186 state = RUNNING;
187 mummy.resumeThread();
188 }
189 }
190 else if (event.getSource() == progress.close_button) {
191 if(state == RUNNING) {
192 previous_state = state;
193 state = STOPPED; // do we need to do anything else to stop this?
194 }
195 mummy.deleteDownloadJob(this);
196 }
197 }
198
199 /** Depending on which button on the progress bar was pushed,
200 * this method will affect the state of the DownloadJob and perhaps make
201 * calls to wget.class if necessary.
202 * @param event The ActionEvent fired from within the DownloadProgressBar
203 * which we must respond to.
204 * Now using synchronized methods like previous_state = getState(); instead of
205 * previous_state = state; and setState(STOPPED); instead of state = STOPPED;
206 */
207 public void actionPerformed(ActionEvent event) {
208 // The stop_start_button is used to alternately start or stop the
209 // job. If the current state of the job is paused then this
210 // restart is logically equivalent to a resume.
211 if(event.getSource() == progress.stop_start_button) {
212 previous_state = getState();
213 if (getState() == RUNNING) {
214 stopDownload(); // cancels any running SafeProcess, will set the current state to STOPPED when the time is right
215 } else {
216 setState(RUNNING);
217 mummy.resumeThread();
218 }
219 }
220 else if (event.getSource() == progress.close_button) {
221 setClosed();
222 SafeProcess.log("@@@ Progress bar close button pressed");
223 if(getState() == RUNNING) {
224 previous_state = getState();
225 stopDownload(); // cancels any running SafeProcess, will set the current state to STOPPED when the time is right
226 }
227 mummy.deleteDownloadJob(this);
228 }
229 }
230
231 /** Given a portnumber to check, returns true if it is available
232 * (if nothing's listening there already). */
233 public static boolean isPortAvailable(int portnum) {
234 Socket tmpSocket = null;
235 try {
236 tmpSocket = new Socket("localhost", portnum);
237 tmpSocket.close();
238 return false;
239
240 } catch(ConnectException ex){
241 // "Signals that an error occurred while attempting to connect a socket
242 // to a remote address and port. Typically, the connection was refused
243 // remotely (e.g., no process is listening on the remote address/port)."
244 System.err.println("Port " + portnum + " not yet in use.");
245 tmpSocket = null;
246 return true;
247
248 } catch(Exception ex) {
249 // includes BindException "Signals that an error occurred while attempting
250 // to bind a socket to a local address and port. Typically, the port is in
251 // use, or the requested local address could not be assigned."
252 tmpSocket = null;
253 return false;
254 }
255 }
256
257 /** Circular buffer. Modifies the value of nextFreePort (the buffer index). */
258 private void incrementNextFreePort() {
259 int offset = nextFreePort - PORT_BASE;
260 offset = (offset + 1) % PORT_BLOCK_SIZE;
261 nextFreePort = PORT_BASE + offset;
262 }
263
264 // If eschewing the use of SafeProcess, reactivate (by renaming) old_callDownload()
265 // and old_actionPerformed(), and DownloadScrollPane.java's old_deleteDownloadJob().
266 public void old_callDownload() {
267
268 ArrayList command_list = new ArrayList();
269
270 // the following also works for client-gli if downloading is enabled (when there's a gs2build directory inside gli)
271 command_list.add(Configuration.perl_path);
272 command_list.add("-S");
273 command_list.add(LocalGreenstone.getBinScriptDirectoryPath()+"downloadfrom.pl");
274 command_list.add("-download_mode");
275 command_list.add(mode);
276 command_list.add("-cache_dir");
277 command_list.add(Gatherer.getGLIUserCacheDirectoryPath());
278 // For the purposes of prematurely terminating wget from GLI (which creates a socket
279 // as a communication channel between GLI and Perl), it is important to tell the script
280 // that we're running as GLI. Because when running from the command prompt, it should
281 // not create this socket and do the related processing.
282 command_list.add("-gli");
283
284 ArrayList all_arg = download.getArguments(true,false);
285 for(int i = 0; i < all_arg.size(); i++) {
286 Argument argument = (Argument) all_arg.get(i);
287 if(argument.isAssigned()) {
288 command_list.add("-" + argument.getName());
289 if(argument.getType() != Argument.FLAG) {
290 command_list.add(argument.getValue());
291 }
292 }
293 }
294
295 String [] cmd = (String []) command_list.toArray(new String[0]);
296 DebugStream.println("Download job, "+command_list);
297
298 if (previous_state == DownloadJob.COMPLETE) {
299 progress.mirrorBegun(true, true);
300 }
301 else {
302 progress.mirrorBegun(false, true);
303 }
304
305 try {
306 Runtime rt = Runtime.getRuntime();
307
308 String [] env = null;
309
310 Process prcs = null;
311
312 if (proxy_url != null && !proxy_url.equals("")) {
313 // Specify proxies as environment variables
314 // Need to manually specify GSDLHOME and GSDLOS also
315
316 proxy_url = proxy_url.replaceAll("http://","");
317
318 env = new String[7];
319
320 env[0] = "http_proxy=http://"+proxy_url;
321 env[1] = "https_proxy=http://"+proxy_url; // yes, HTTP protocol for https:// too
322 // see also https://wiki.archlinux.org/index.php/proxy_settings
323 env[2] = "ftp_proxy=ftp://"+proxy_url;
324 env[3] = "GSDLHOME=" + Configuration.gsdl_path;
325 env[4] = "GSDLOS=" + Gatherer.client_operating_system;
326 // teach it where the wgetrc file lives, in gs2build/bin/<os>:
327 env[5] = "WGETRC=" + LocalGreenstone.getBinOSDirectoryPath(Gatherer.client_operating_system)+"wgetrc";
328 // Issue discovered on Windows: If PATH is not made available to perl, so that wget or something else needed by
329 // WgetDownload.pm's open3() call is not on the PATH, then the perl open() call to run wget fails.
330 // So make PATH available to get open3() working:
331 env[6] = "PATH="+System.getenv("PATH");
332
333 prcs = rt.exec(cmd, env);
334 }
335 else if(Gatherer.isGsdlRemote && Gatherer.isDownloadEnabled && !Utility.isWindows()) {
336 // Not Windows, but running client with download panel
337 // Need to manually specify GSDLHOME and GSDLOS
338 env = new String[3];
339 env[0] = "GSDLHOME=" + Configuration.gsdl_path;
340 env[1] = "GSDLOS=" + Gatherer.client_operating_system;
341 env[2] = "WGETRC=" + LocalGreenstone.getBinOSDirectoryPath(Gatherer.client_operating_system)+"wgetrc"; // teach it where the wgetrc file lives, in gs2build/bin/<os>
342 prcs = rt.exec(cmd, env);
343 }
344 else {
345 // Will inherit the GLI's environment, with GSDLHOME and GSDLOS set
346 prcs = rt.exec(cmd);
347 }
348 //System.out.println(newcmd);
349
350 // Can use the following if debugging WgetDownload.pm - Reads debug stmts from the perl process' STDIN stream
351 //(new PerlReaderThread(prcs)).start();
352
353 InputStream is = prcs.getInputStream();
354 BufferedReader reader = new BufferedReader(new InputStreamReader(is));
355
356 // To be able to stop Wget, we use sockets to communicate with the perl process that launched wget
357 if (mode.equals("Web") || mode.equals("MediaWiki")) { // wget download modes other than OAI
358
359 // Need to find an available (unused) port within the range we're looking for to pass it
360 // the Perl child process, so that it may set up a listening ServerSocket at that port number
361 try {
362 boolean foundFreePort = false;
363 for(int i = 0; i < PORT_BLOCK_SIZE; i++) {
364
365 if(isPortAvailable(nextFreePort)) {
366 foundFreePort = true;
367 break;
368
369 } else {
370 incrementNextFreePort();
371 }
372 }
373
374 if(foundFreePort) {
375 // Free port number currently found becomes the port number of the socket that this
376 // DownloadJob instance will be connecting to when the user wants to prematurely stop Wget.
377 this.port = nextFreePort;
378 incrementNextFreePort();
379
380 } else {
381 throw new Exception("Cannot find an available port in the range "
382 + PORT_BASE + "-" + (PORT_BASE+PORT_BLOCK_SIZE)
383 + "\nwhich is necessary for forcibly terminating wget.");
384 }
385
386 // Communicate the chosen port for this DownloadJob instance to the perl process, so
387 // that it can set up a ServerSocket at that port to listen for any signal to terminate wget
388 OutputStream os = prcs.getOutputStream();
389 String p = ""+this.port+"\n";
390 System.err.println("Portnumber found: " + p);
391
392 os.write(p.getBytes());
393 os.close();
394
395 } catch(Exception ex) {
396 System.err.println("Sent available portnumber " + this.port + " to process' outputstream.\nBut got exception: " + ex);
397 }
398 }
399
400 BufferedReader br = new BufferedReader(new InputStreamReader(prcs.getErrorStream()));
401 // Capture the standard error stream and search for two particular occurrences.
402 String line="";
403 boolean ignore_for_robots = false;
404 int max_download = DownloadJob.UNKNOWN_MAX;
405
406 while ((line = br.readLine()) != null && !line.trim().equals("<<Finished>>") && state != STOPPED) {
407 if ( max_download == DownloadJob.UNKNOWN_MAX) {
408 if(line.lastIndexOf("<<Defined Maximum>>") != -1) {
409 max_download = DownloadJob.DEFINED_MAX;
410 }
411 else if (line.lastIndexOf("<<Undefined Maximum>>") != -1) {
412 max_download = DownloadJob.UNDEFINED_MAX;
413 }
414 }
415 else if(max_download == DownloadJob.UNDEFINED_MAX) {
416 DebugStream.println(line);
417 download_log.appendLine(line);
418 // The first magic special test is to see if we've just
419 // asked for the robots.txt file. If so we ignore
420 // the next add and then the next complete/error.
421 if(line.lastIndexOf("robots.txt;") != -1) {
422 DebugStream.println("***** Requesting robot.txt");
423 ignore_for_robots = true;
424 }
425 // If line contains "=> `" display text as the
426 // currently downloading url. Unique to add download.
427 else if(line.lastIndexOf("=> `") != -1) {
428 if(!ignore_for_robots) {
429 // Add download
430 String new_url = line.substring(line.indexOf("`") + 1, line.lastIndexOf("'"));
431 progress.addDownload("file"); //addDownload("http:/" + new_url.substring(cachedir_prefix_length()-1));
432 }
433 }
434 // If line contains "/s) - `" set currently
435 // downloading url to "Download Complete".
436 else if(line.lastIndexOf("/s) - `") != -1) {
437 String current_file_downloading = line.substring(line.indexOf("`") + 1, line.lastIndexOf("'"));
438 if(!ignore_for_robots) {
439 DebugStream.println("Not ignore for robots");
440 // Download complete
441 downloadComplete(current_file_downloading);
442 }
443 else {
444 DebugStream.println("Ignore for robots");
445 ignore_for_robots = false;
446 }
447 }
448 // The already there line begins "File `..." However this
449 // is only true in english, so instead I looked and there
450 // are few (if any at all) other messages than those above
451 // and not overwriting messages that use " `" so we'll
452 // look for that. Note this method is not guarenteed to be
453 // unique like the previous two.
454 else if(line.lastIndexOf(" `") != -1) {
455 // Not Overwriting
456 DebugStream.println("Already there.");
457 String new_url =
458 line.substring(line.indexOf("`") + 1, line.lastIndexOf("'"));
459 progress.addDownload("file"); //addDownload("http:/" + new_url.substring(cachedir_prefix_length()-1));
460 downloadWarning();
461 }
462 // Any other important message starts with the time in the form hh:mm:ss
463 else if(line.length() > 7) {
464 if(line.charAt(2) == ':' && line.charAt(5) == ':') {
465 if(!ignore_for_robots) {
466 DebugStream.println("Error.");
467 downloadFailed();
468 }
469 else {
470 ignore_for_robots = false;
471 }
472 }
473 }
474 }
475 else if (max_download == DownloadJob.DEFINED_MAX) {
476 if (line.lastIndexOf("<<Total number of record(s):") != -1) {
477 String total_ID = line.substring(line.indexOf(":") + 1, line.indexOf(">"));
478 progress.setTotalDownload((Integer.valueOf(total_ID)).intValue());
479 progress.resetFileCount();
480 progress.addDownload("files"); // for display: "Downloading files"
481 }
482 else if (line.lastIndexOf("<<Done>>") != -1) {
483 progress.increaseFileCount();
484 }
485 else if(line.lastIndexOf("<<Done:") != -1) {
486 String completed_amount = line.substring(line.indexOf(":") + 1, line.indexOf(">"));
487 progress.increaseFileCount((Integer.valueOf(completed_amount)).intValue());
488 }
489
490 DebugStream.println(line);
491 download_log.appendLine(line);
492 }
493 else {
494 System.out.println("Error!!");
495 System.exit(-1);
496 }
497 }
498
499 if(state == STOPPED) {
500 boolean terminatePerlScript = true;
501
502 // When GLI is working with wget-based download modes other than OAI (MediaWiki and Web
503 // download) and the STOP button has been pressed, wget needs to be prematurely terminated.
504 // Only wget download modes Web and MediaWiki require the use of sockets to communicate
505 // with the perl script in order to get wget to terminate. Other download modes, including
506 // wgetdownload mode OAI, can terminate in the traditional manner: close process inputstream
507 // and kill perl process. OAI launches many wgets. So that when the perl process is terminated,
508 // the currently running wget will finish off but other wgets are no longer launched.
509 if(prcs != null && (mode.equals("Web") || mode.equals("MediaWiki"))) {
510
511 // create a socket to the perl child process and communicate the STOP message
512 Socket clientSocket = null;
513 if(clientSocket == null) {
514 try {
515 clientSocket = new Socket("localhost", this.port); // connect to the port chosen for this DownloadJob instance
516
517 BufferedReader clientReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
518 String response = clientReader.readLine(); // see if we've been connected
519 System.err.println("Communicating with perl download script on port " + this.port
520 + "\nGot response from perl: " + response);
521
522 // Send the STOP signal
523 OutputStream os = clientSocket.getOutputStream();
524 String message = "<<STOP>>\n";
525 os.write(message.getBytes());
526 response = clientReader.readLine(); // see whether the stop signal has been received
527 System.err.println("GLI sent STOP signal to perl to terminate wget."
528 + "\nGot response from perl: " + response);
529
530 response = clientReader.readLine(); // see whether the perl script is ready to be terminated
531 System.err.println("Got another response from perl: " + response);
532 os.close();
533
534 clientReader.close();
535 clientSocket.close(); // close the clientSocket (the Perl end will close the server socket that Perl opened)
536 clientReader = null;
537 clientSocket = null;
538
539 if(response == null) {
540 terminatePerlScript = false;
541 }
542 } catch(IOException ex) {
543 System.err.println("Tried to communicate through client socket - port " + this.port + ", but got exception: " + ex);
544 } catch(Exception ex) {
545 System.err.println("Tried to open client socket, but got exception: " + ex);
546 }
547 }
548 }
549
550 //prcs.getInputStream().close();
551 prcs.getErrorStream().close();
552 br.close();
553 br = null;
554 if(terminatePerlScript) {
555 prcs.destroy(); // This doesn't always work, but it's worth a try
556 prcs = null;
557 }
558
559 // Notify the DownloadScrollPane which is waiting on this job to complete that we are ready
560 synchronized(this) {
561 this.notify();
562 }
563 }
564 }
565 catch (Exception ioe) {
566 //message(Utility.ERROR, ioe.toString());
567 //JTest
568 DebugStream.printStackTrace(ioe);
569 }
570 // If we've got to here and the state isn't STOPPED then the
571 // job is complete.
572 if(state == DownloadJob.RUNNING) {
573 progress.mirrorComplete();
574 previous_state = state;
575 state = DownloadJob.COMPLETE;
576 }
577 // refresh the workspace tree
578 Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.DOWNLOADED_FILES_CHANGED);
579 }
580
581 public void callDownload() {
582
583 ArrayList command_list= new ArrayList();
584
585 // the following also works for client-gli if downloading is enabled (when there's a gs2build directory inside gli)
586 command_list.add(Configuration.perl_path);
587 command_list.add("-S");
588 command_list.add(LocalGreenstone.getBinScriptDirectoryPath()+"downloadfrom.pl");
589 command_list.add("-download_mode");
590 command_list.add(mode);
591 command_list.add("-cache_dir");
592 command_list.add(Gatherer.getGLIUserCacheDirectoryPath());
593 // For the purposes of prematurely terminating wget from GLI (which creates a socket
594 // as a communication channel between GLI and Perl), it is important to tell the script
595 // that we're running as GLI. Because when running from the command prompt, it should
596 // not create this socket and do the related processing.
597 command_list.add("-gli");
598
599 ArrayList all_arg = download.getArguments(true,false);
600 for(int i = 0; i < all_arg.size(); i++) {
601 Argument argument = (Argument) all_arg.get(i);
602 if(argument.isAssigned()) {
603 command_list.add("-" + argument.getName());
604 if(argument.getType() != Argument.FLAG) {
605 command_list.add(argument.getValue());
606 }
607 }
608 }
609
610 String [] cmd = (String []) command_list.toArray(new String[0]);
611 DebugStream.println("Download job, "+command_list);
612
613 if (previous_state == DownloadJob.COMPLETE) {
614 progress.mirrorBegun(true, true);
615 }
616 else {
617 progress.mirrorBegun(false, true);
618 }
619
620 try {
621 Runtime rt = Runtime.getRuntime();
622
623 String [] env = null;
624
625 if (proxy_url != null && !proxy_url.equals("")) {
626 // Specify proxies as environment variables
627 // Need to manually specify GSDLHOME and GSDLOS also
628
629 proxy_url = proxy_url.replaceAll("http://","");
630
631 env = new String[7];
632
633 env[0] = "http_proxy=http://"+proxy_url;
634 env[1] = "https_proxy=http://"+proxy_url; // yes, HTTP protocol for https:// too
635 // see also https://wiki.archlinux.org/index.php/proxy_settings
636 env[2] = "ftp_proxy=ftp://"+proxy_url;
637 env[3] = "GSDLHOME=" + Configuration.gsdl_path;
638 env[4] = "GSDLOS=" + Gatherer.client_operating_system;
639 // teach it where the wgetrc file lives, in gs2build/bin/<os>:
640 env[5] = "WGETRC=" + LocalGreenstone.getBinOSDirectoryPath(Gatherer.client_operating_system)+"wgetrc";
641 // Issue discovered on Windows: If PATH is not made available to perl, so that wget or something else needed by
642 // WgetDownload.pm's open3() call is not on the PATH, then the perl open() call to run wget fails.
643 // So make PATH available to get open3() working:
644 env[6] = "PATH="+System.getenv("PATH");
645
646 prcs = new SafeProcess(cmd, env, null);
647 }
648 else if(Gatherer.isGsdlRemote && Gatherer.isDownloadEnabled && !Utility.isWindows()) {
649 // Not Windows, but running client with download panel
650 // Need to manually specify GSDLHOME and GSDLOS
651 env = new String[3];
652 env[0] = "GSDLHOME=" + Configuration.gsdl_path;
653 env[1] = "GSDLOS=" + Gatherer.client_operating_system;
654 env[2] = "WGETRC=" + LocalGreenstone.getBinOSDirectoryPath(Gatherer.client_operating_system)+"wgetrc"; // teach it where the wgetrc file lives, in gs2build/bin/<os>
655 prcs = new SafeProcess(cmd, env, null);
656 }
657 else {
658 // Will inherit the GLI's environment, with GSDLHOME and GSDLOS set
659 prcs = new SafeProcess(cmd);
660 }
661
662 //System.out.println(newcmd);
663 prcs.setMainHandler(this); // attach handler to clean up before and after process.destroy()
664 // for which DownloadJob implements SafeProcess.MainProcessHandler
665
666 // To be able to stop Wget, we use sockets to communicate with the perl process that launched wget
667 if (mode.equals("Web") || mode.equals("MediaWiki")) { // wget download modes other than OAI
668
669 // Need to find an available (unused) port within the range we're looking for to pass it
670 // the Perl child process, so that it may set up a listening ServerSocket at that port number
671 try {
672 boolean foundFreePort = false;
673 for(int i = 0; i < PORT_BLOCK_SIZE; i++) {
674
675 if(isPortAvailable(nextFreePort)) {
676 foundFreePort = true;
677 break;
678
679 } else {
680 incrementNextFreePort();
681 }
682 }
683
684 if(foundFreePort) {
685 // Free port number currently found becomes the port number of the socket that this
686 // DownloadJob instance will be connecting to when the user wants to prematurely stop Wget.
687 this.port = nextFreePort;
688 incrementNextFreePort(); //// Necessary?
689
690 } else {
691 throw new Exception("Cannot find an available port in the range "
692 + PORT_BASE + "-" + (PORT_BASE+PORT_BLOCK_SIZE)
693 + "\nwhich is necessary for forcibly terminating wget.");
694 }
695
696 // Communicate the chosen port for this DownloadJob instance to the perl process, so
697 // that it can set up a ServerSocket at that port to listen for any signal to terminate wget
698 //OutputStream os = prcs.getOutputStream();
699 String p = ""+this.port+"\n";
700 System.err.println("Portnumber found: " + p);
701
702 prcs.setInputString(p);
703
704 } catch(Exception ex) {
705 System.err.println("Sent available portnumber " + this.port + " to process' outputstream.\nBut got exception: " + ex);
706 }
707 }
708
709 ProcessErrHandler errHandler = new ProcessErrHandler(); // meaningful output comes from prcs stderr
710 ProcessOutHandler outHandler = new ProcessOutHandler(); // debugging output comes from prcs' stdout
711
712 int exitVal = prcs.runProcess(null, outHandler, errHandler);
713
714 // if prcs is interrupted (cancelled) during the blocking runProcess() call,
715 // as happens on state == STOPPED, then
716 // beforeWaitingForStreamsToEnd() is called before the process' worker threads come to a halt
717 // and afterStreamsEnded() is called when the process' worker threads have halted,
718 // beforeProcessDestroy() is called before the process is destroyed,
719 // and afterProcessDestroy() is called after the proc has been destroyed.
720 // If when beforeWaitingForStreamsEnd() stage the perl was still running but had been
721 // told to stop, then the beforeWaitingForStreamsEnd() method will make sure to communicate
722 // with the perl process over a socket and send it the termination message,
723 // which will also kill any runnning wget that perl launched.
724 // In that case, destroy() is actually called on the process at last.
725
726 }
727 catch (Exception ioe) {
728 SafeProcess.log(ioe);
729 DebugStream.printStackTrace(ioe);
730 }
731
732 // now the process is done, we can at last null it
733 prcs = null;
734
735 // If we've got to here and the state isn't STOPPED then the
736 // job is complete.
737 if(getState() == DownloadJob.RUNNING) {
738 progress.mirrorComplete();
739 previous_state = getState();
740 setState(DownloadJob.COMPLETE);
741 }
742
743 SafeProcess.log("@@@@ DONE callDownload()");
744
745 // refresh the workspace tree
746 Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.DOWNLOADED_FILES_CHANGED);
747 }
748
749 private synchronized boolean isStopped() { return state == STOPPED; }
750
751 // called when the user cancelled the download and we're told to stop both our external perl process
752 // and the wget process that it in turn launched
753 public void stopDownload() {
754 if(prcs != null) {
755 SafeProcess.log("@@@ Going to cancel the SafeProcess...");
756
757 // Whether a process ends naturally or is prematurely ended, beforeWaitingForStreamsToEnd()
758 // will be called. We've hooked this in to calling tellPerlToTerminateWget() only if the
759 // process is still running when cancel is pressed, but not when it's naturally terminated.
760 boolean hadToSendInterrupt = prcs.cancelRunningProcess(); // returns false if it was already terminating/terminated, true if interrupt sent
761
762 } else {
763 System.err.println("@@@@ No SafeProcess to cancel");
764 }
765
766 //setState(STOPPED); // would set it to stop on cancel, even if it already naturally terminated
767
768 }
769
770//*********** START of implementing interface Safeprocess.MainProcessHandler
771 // before and after processDestroy only happen when interrupted AND terminatePerlScript=true
772 public void beforeProcessDestroy() {}
773 public void afterProcessDestroy() {}
774
775 // after blocking call on closing up streamgobbler worker threads that happens
776 // upon natural termination or interruption of process' main body/thread.
777 // if not overriding, then return the parameter forciblyTerminating as-is
778 public boolean afterStreamsEnded(boolean forciblyTerminating) { return forciblyTerminating; }
779
780 // called after the SafeProcess has fully terminated (naturally or via process.destroy())
781 // and has been cleaned up
782 public void doneCleanup(boolean wasForciblyTerminated) {
783 // let the user know they can cancel again now cleanup phase is done
784 progress.enableCancelJob(true);
785
786 if(wasForciblyTerminated) {
787 setState(STOPPED); // sets it to stop only if process truly was prematurely terminated, not merely
788 // if the cancel button was clicked when it had already naturally terminated
789
790 // If the user had pressed the Close button to terminate the running job, then
791 // we're now ready to remove the display of the until now running job
792 // from the download progress bar interface
793 // But don't bother removing the progress bar if the user had only pressed the Stop button
794 if(wasClosed()) {
795 mummy.deleteCurrentDownloadJob(this);
796 }
797 }
798 }
799
800 // before blocking call of ending streamgobbler worker threads that happens
801 // after process' main body/thread has naturally terminated or been interrupted
802 public boolean beforeWaitingForStreamsToEnd(boolean forciblyTerminating) {
803 // let the user know they can't cancel during cleanup phase
804 progress.enableCancelJob(false);
805
806 SafeProcess.log("**** in beforeWaitingForStreamsToEnd()");
807
808 // state would not be STOPPED if cancel was pressed after the process naturally terminated anyway
809 // in that case we don't need to send perl the signal to terminate WGET
810 if(!forciblyTerminating) { //if(!isStopped()) {
811 SafeProcess.log("*** Process not (yet) cancelled/state not (yet) stopped");
812 SafeProcess.log("*** But process has naturally terminated (process streams are being closed before any interruption signal can be received), so won't be destroying process even on interrupt");
813 return false; // for us to be in this method at all with forciblyTerminating being false
814 // means the process is already naturally terminating, so don't unnaturally destroy it
815 }
816
817 // else the process is still running and we've been told to stop, so tell perl to stop wget first
818 // (so that process destroy can then be called thereafter)
819 return tellPerlToTerminateWget();
820 }
821//*********** END of implementing interface Safeprocess.MainProcessHandler
822
823 public boolean tellPerlToTerminateWget() {
824 SafeProcess.log("**** in tellPerlToTerminateWget()");
825
826 boolean terminatePerlScript = true;
827
828 // When GLI is working with wget-based download modes other than OAI (MediaWiki and Web
829 // download) and the STOP button has been pressed, wget needs to be prematurely terminated.
830 // Only wget download modes Web and MediaWiki require the use of sockets to communicate
831 // with the perl script in order to get wget to terminate. Other download modes, including
832 // wgetdownload mode OAI, can terminate in the traditional manner: close process inputstream
833 // and kill perl process. OAI launches many wgets. So that when the perl process is terminated,
834 // the currently running wget will finish off but other wgets are no longer launched.
835 if((mode.equals("Web") || mode.equals("MediaWiki"))) {
836 SafeProcess.log("@@@ Socket communication to end wget");
837 // create a socket to the perl child process and communicate the STOP message
838 Socket clientSocket = null;
839 BufferedReader clientReader = null;
840 OutputStream os = null;
841
842 if(clientSocket == null) {
843 try {
844 clientSocket = new Socket("localhost", this.port); // connect to the port chosen for this DownloadJob instance
845
846 clientReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
847 String response = clientReader.readLine(); // see if we've been connected
848 System.err.println("Communicating with perl download script on port " + this.port
849 + "\nGot response from perl: " + response);
850
851 // Send the STOP signal
852 os = clientSocket.getOutputStream();
853 String message = "<<STOP>>\n";
854 os.write(message.getBytes());
855 response = clientReader.readLine(); // see whether the stop signal has been received
856 System.err.println("GLI sent STOP signal to perl to terminate wget."
857 + "\nGot response from perl: " + response);
858
859 response = clientReader.readLine(); // see whether the perl script is ready to be terminated
860 System.err.println("Got another response from perl: " + response);
861
862 if(response == null) { // why? Is it because the process has already terminated naturally if response is null?
863 terminatePerlScript = false;
864 }
865 } catch(IOException ex) {
866 if(ex instanceof IOException && ex.getMessage().indexOf("Connection refused") != -1) {
867 terminatePerlScript = false; // no socket listening on other end because process ended
868 System.err.println("Tried to communicate through client socket - port " + this.port + ", but the process seems to have already ended naturally");
869 } else {
870 System.err.println("Tried to communicate through client socket - port " + this.port + ", but got exception: " + ex);
871 }
872
873 } catch(Exception ex) {
874 System.err.println("Tried to open client socket, but got exception: " + ex);
875 } finally {
876 SafeProcess.closeResource(os);
877 SafeProcess.closeResource(clientReader);
878 SafeProcess.closeSocket(clientSocket); // close the clientSocket (the Perl end will close the server socket that Perl opened)
879 os = null;
880 clientReader = null;
881 clientSocket = null;
882 }
883 }
884 }
885
886 return terminatePerlScript; // if true, it will call destroy() on the SafeProcess' process
887 }
888
889
890 /** Called by the WGet native code when the current download is
891 * completed. In turn all download listeners are informed.
892 */
893 public void downloadComplete() {
894 progress.downloadComplete(); // now this is synchronized
895 }
896
897
898 public void downloadComplete(String current_file_downloading)
899 {
900 progress.downloadComplete(); // now this is synchronized
901 DebugStream.println("Download complete: " + current_file_downloading);
902 }
903
904
905 /** Called by the WGet native code when the requested download returns
906 * a status code other than 200.
907 */
908 public void downloadFailed() {
909 // TODO!!
910 //synchronized(failed_urls) {
911 //failed_urls.add(current_url); // It is the current url that failed
912 //}
913 progress.downloadFailed(); // now this is synchronized
914 //DebugStream.println("Download failed: " + current_url);
915 }
916
917 /**
918 */
919 public void downloadWarning() {
920 progress.downloadWarning(); // now this is synchronized
921 }
922
923 public AppendLineOnlyFileDocument getLogDocument() {
924 return download_log;
925 }
926
927 /**
928 * @return Returns the progress bar associated with this job.
929 */
930 public DownloadProgressBar getProgressBar() {
931 return progress;
932 }
933
934 /** Called to discover if the user wanted this thread to run or if
935 * it is paused.
936 * @return An int representing the current DownloadJob state.
937 */
938 public synchronized int getState() {
939 return state;
940 }
941
942 /** @return true if the close button of the DownloadProgressBar was pressed,
943 * false otherwise such as if the Stop button had been pressed.
944 */
945 private synchronized boolean wasClosed() {
946 return this.wasClosed;
947 }
948
949 /** Returns the current state of the stop flag for this job.
950 * @return A boolean representing whether the user has requested to
951 * stop.
952 */
953 public synchronized boolean hasSignalledStop() {
954 if(state == DownloadJob.STOPPED || state == DownloadJob.PAUSED ||
955 state == DownloadJob.COMPLETE) {
956 return true;
957 }
958 return false;
959 }
960
961 public synchronized void setState(int state) {
962 previous_state = this.state;
963 this.state = state;
964 }
965
966 private synchronized void setClosed() {
967 this.wasClosed = true;
968 }
969
970 /** A convenience call.
971 * @return A String representing the url of the initial url (root node of the mirrored tree).
972 */
973 public String toString() {
974 return download_url;
975 }
976
977 /** Called by the WGet native code to signal the current progress of
978 * downloading.
979 * @param current A long representing the number of bytes that have
980 * been downloaded since the last update.
981 * @param expected A long representing the total number of bytes
982 * expected for this download.
983 */
984 public void updateProgress(long current, long expected) {
985 progress.updateProgress(current, expected);
986 }
987
988
989 /*
990 Go through https://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html series of
991 Java articles on concurrency again.
992 Go through http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
993
994 http://stackoverflow.com/questions/574240/is-there-an-advantage-to-use-a-synchronized-method-instead-of-a-synchronized-blo
995
996 "Not only do synchronized methods not lock the whole class, but they don't lock the whole instance either. Unsynchronized methods in the class may still proceed on the instance."
997 "Only the syncronized methods are locked. If there are fields you use within synced methods that are accessed by unsynced methods, you can run into race conditions."
998
999 "synchronizing on "this" is considered in some circles to be an anti-pattern. The unintended consequence is that outside of the class someone can lock on an object reference that is equal to "this" and prevent other threads from passing the barriers within the class potentially creating a deadlock situation. Creating a "private final Object = new Object();" variable purely for locking purposes is the often used solution. Here's another question relating directly to this issue. http://stackoverflow.com/questions/442564/avoid-synchronizedthis-in-java?lq=1"
1000
1001 "A private lock is a defensive mechanism, which is never a bad idea.
1002
1003 Also, as you alluded to, private locks can control granularity. One set of operations on an object might be totally unrelated to another but synchronized(this) will mutually exclude access to all of them."
1004
1005 http://stackoverflow.com/questions/8393883/is-synchronized-keyword-exception-safe
1006 "In any scoped thread-safe block, the moment you get out of it, the thread-safety is gone."
1007 "In case of an exception the lock will be released."
1008
1009 http://stackoverflow.com/questions/8259479/should-i-synchronize-listener-notifications-or-not
1010 "Use a CopyOnWriteArrayList for your listener arrays."
1011 "If you use the CopyOnWriteArrayList, then you don't have to synchronize when iterating."
1012 "CopyOnWriteArrayList is thread-safe, so there is no need to synchronize."
1013
1014 "Use a ConcurrentLinkedQueue<Listener> ... for this kind of problems: adding, removing and iterating simultaneously on a collection.
1015 A precision : this solution prevents a listener from being called from the very moment it is deregistered."
1016 "It means that you start iterating, an element is added, it will be called, another is removed, it won't, all this in the same iteration cycle.
1017 It's the best of both world: ensuring synchronization, while being fine grained on who gets called and who's not."
1018
1019 http://stackoverflow.com/questions/8260205/when-a-listener-is-removed-is-it-okay-that-the-event-be-called-on-that-listener
1020
1021 http://stackoverflow.com/questions/2282166/java-synchronizing-on-primitives
1022
1023 1. You can't lock on a primitive and
1024 2. Don't lock on a Long unless you're careful how you construct them. Long values created by autoboxing or Long.valueOf() in a certain range are guaranteed to be the same across the JVM which means other threads could be locking on the same exact Long object and giving you cross-talk. This can be a subtle concurrency bug (similar to locking on intern'ed strings).
1025
1026 Cross-talk:
1027 "In electronics, crosstalk is any phenomenon by which a signal transmitted on one circuit or channel of a transmission system creates an undesired effect in another circuit or channel. Crosstalk is usually caused by undesired capacitive, inductive, or conductive coupling from one circuit, part of a circuit, or channel, to another."
1028 */
1029
1030
1031 // Inner thread class that reads from process downloadfrom.pl's std output stream
1032 private class ProcessOutHandler extends SafeProcess.CustomProcessHandler {
1033
1034 public ProcessOutHandler() {
1035 super(SafeProcess.STDOUT);
1036 }
1037
1038 public void run(Closeable stream) {
1039 InputStream is = (InputStream) stream;
1040 BufferedReader eReader = null;
1041 try {
1042
1043 String message = null;
1044 eReader = new BufferedReader(new InputStreamReader(is));
1045 while(!Thread.currentThread().isInterrupted() && (message = eReader.readLine()) != null) {
1046 if(!message.equals("\n")) {
1047 System.err.println("**** Perl STDOUT: " + message);
1048 }
1049 }
1050 if(Thread.currentThread().isInterrupted()) {
1051 System.err.println("**** Perl INTERRUPTed.");
1052 } else {
1053 System.err.println("**** Perl ENDed.");
1054 }
1055
1056 } catch(Exception e) {
1057 System.err.println("Thread - caught exception: " + e);
1058 } finally {
1059 if(Thread.currentThread().isInterrupted()) {
1060 SafeProcess.log("@@@ Successfully interrupted " + Thread.currentThread().getName() + ".");
1061 }
1062 SafeProcess.closeResource(eReader);
1063 eReader = null;
1064 }
1065 }
1066 }
1067
1068
1069 private class ProcessErrHandler extends SafeProcess.CustomProcessHandler {
1070
1071 public ProcessErrHandler() {
1072 super(SafeProcess.STDERR);
1073 }
1074
1075 public void run(Closeable stream) {
1076 InputStream eis = (InputStream) stream;
1077
1078 BufferedReader br = null;
1079 try {
1080 br = new BufferedReader(new InputStreamReader(eis));
1081
1082 // Capture the standard error stream and search for two particular occurrences.
1083 String line="";
1084 boolean ignore_for_robots = false;
1085 int max_download = DownloadJob.UNKNOWN_MAX;
1086
1087 // handle to outer class objects that need synchronization (on either objects or their methods)
1088 DownloadProgressBar progress = DownloadJob.this.progress;
1089 AppendLineOnlyFileDocument download_log = DownloadJob.this.download_log;
1090
1091 boolean noCheckCertificate_needed = false; // determine whether this flag needs to be set for wget (under Preferences > Connection)
1092
1093 while (!Thread.currentThread().isInterrupted() && (line = br.readLine()) != null
1094 && !line.trim().equals("<<Finished>>") /*&& !isStopped()*/) {
1095 if (max_download == DownloadJob.UNKNOWN_MAX) {
1096 if(line.lastIndexOf("<<Defined Maximum>>") != -1) {
1097 max_download = DownloadJob.DEFINED_MAX;
1098 }
1099 else if (line.lastIndexOf("<<Undefined Maximum>>") != -1) {
1100 max_download = DownloadJob.UNDEFINED_MAX;
1101 }
1102 }
1103 else if(max_download == DownloadJob.UNDEFINED_MAX) {
1104 DebugStream.println(line);
1105 download_log.appendLine(line); // now synchronized
1106 // The first magic special test is to see if we've just
1107 // asked for the robots.txt file. If so we ignore
1108 // the next add and then the next complete/error.
1109 if(line.lastIndexOf("robots.txt;") != -1) {
1110 DebugStream.println("***** Requesting robot.txt");
1111 ignore_for_robots = true;
1112 }
1113 else if(line.indexOf("--no-check-certificate") != -1) {
1114 downloadWarning();
1115 noCheckCertificate_needed = true;
1116 }
1117 // If line contains "=> `" display text as the
1118 // currently downloading url. Unique to add download.
1119 else if(line.lastIndexOf("=> `") != -1) {
1120 if(!ignore_for_robots) {
1121 // Add download
1122 String new_url = line.substring(line.indexOf("`") + 1, line.lastIndexOf("'"));
1123
1124 // now synchronized
1125 progress.addDownload("file"); //addDownload("http:/" + new_url.substring(cachedir_prefix_length()-1));
1126 }
1127 }
1128 // If line contains "/s) - `" set currently
1129 // downloading url to "Download Complete".
1130 // Currently: on windows ` marks start quote of downloaded file, but on linux ' marks it
1131 else if(line.lastIndexOf("/s) - `") != -1 || line.lastIndexOf("/s) - '") != -1) {
1132 String startChar = (line.lastIndexOf("/s) - `") != -1) ? "`" : "'";
1133 String current_file_downloading = line.substring(line.indexOf(startChar) + 1, line.lastIndexOf("'"));
1134 if(!ignore_for_robots) {
1135 DebugStream.println("Not ignore for robots");
1136 // Download complete
1137 downloadComplete(current_file_downloading); // synchronized
1138 }
1139 else {
1140 DebugStream.println("Ignore for robots");
1141 ignore_for_robots = false;
1142 }
1143 }
1144 // The already there line begins "File `..." However this
1145 // is only true in english, so instead I looked and there
1146 // are few (if any at all) other messages than those above
1147 // and not overwriting messages that use " `" so we'll
1148 // look for that. Note this method is not guarenteed to be
1149 // unique like the previous two.
1150 else if(line.lastIndexOf(" `") != -1) {
1151 // Not Overwriting
1152 DebugStream.println("Already there.");
1153 String new_url = line.substring(line.indexOf("`") + 1, line.lastIndexOf("'"));
1154
1155 progress.addDownload("file"); //addDownload("http:/" + new_url.substring(cachedir_prefix_length()-1));
1156 downloadWarning();
1157 }
1158 // Any other important message starts with the time in the form hh:mm:ss
1159 else if(line.length() > 7) {
1160 if(line.charAt(2) == ':' && line.charAt(5) == ':') {
1161 if(!ignore_for_robots) {
1162 DebugStream.println("Error.");
1163 downloadFailed();
1164 }
1165 else {
1166 ignore_for_robots = false;
1167 }
1168 }
1169 }
1170 }
1171 else if (max_download == DownloadJob.DEFINED_MAX) {
1172 if (line.lastIndexOf("<<Total number of record(s):") != -1) {
1173 String total_ID = line.substring(line.indexOf(":") + 1, line.indexOf(">"));
1174
1175 progress.setTotalDownload((Integer.valueOf(total_ID)).intValue());
1176 progress.resetFileCount();
1177 progress.addDownload("files"); // for display: "Downloading files"
1178
1179 }
1180 else if (line.lastIndexOf("<<Done>>") != -1) {
1181 progress.increaseFileCount();
1182 }
1183 else if(line.lastIndexOf("<<Done:") != -1) {
1184 String completed_amount = line.substring(line.indexOf(":") + 1, line.indexOf(">"));
1185 progress.increaseFileCount((Integer.valueOf(completed_amount)).intValue());
1186 }
1187
1188 DebugStream.println(line);
1189 download_log.appendLine(line);
1190 }
1191 else {
1192 System.out.println("Error!!");
1193 System.exit(-1);
1194 }
1195 }
1196 if(noCheckCertificate_needed) { // can't download from (some nested) URL as its
1197 // certificate can't be verified. User must turn on --no-check-certificate
1198 // if they want to download from there anyway, but it will do so insecurely
1199 download_log.appendLine("************");
1200 download_log.appendLine(Dictionary.get("Mirroring.DownloadJob.Warning_No_Valid_Certificate"));
1201 download_log.appendLine(Dictionary.get("Mirroring.DownloadJob.Enable_NoCheckCertificate"));
1202 download_log.appendLine("************");
1203 }
1204
1205 } catch (IOException ioe) {
1206 //message(Utility.ERROR, ioe.toString());
1207 //JTest
1208 DebugStream.printStackTrace(ioe);
1209
1210 } finally {
1211 if(Thread.currentThread().isInterrupted()) { // if the thread this class is running in is interrupted
1212 SafeProcess.log("@@@ Successfully interrupted " + Thread.currentThread().getName() + ".");
1213 }
1214
1215 SafeProcess.closeResource(br);
1216 br = null;
1217 }
1218
1219 }
1220 }
1221}
Note: See TracBrowser for help on using the repository browser.