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

Last change on this file since 31880 was 31880, checked in by ak19, 7 years ago

All the changes that were required to set up multiple proxy servers, one for HTTP, one for HTTPS, one for FTP. Still need to test on Windows

  • Property svn:keywords set to Author Date Id Revision
File size: 49.3 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 Properties proxy_urls; // 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, Properties proxy_urls) {
114 URL url = null;
115 int folder_hash;
116
117 this.proxy_urls = proxy_urls;
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_urls.size() != 0) {
313 // Specify proxies as environment variables
314 // Need to manually specify GSDLHOME and GSDLOS also
315 env = new String[4+proxy_urls.size()];
316
317 env[0] = "GSDLHOME=" + Configuration.gsdl_path;
318 env[1] = "GSDLOS=" + Gatherer.client_operating_system;
319 // teach it where the wgetrc file lives, in gs2build/bin/<os>:
320 env[2] = "WGETRC=" + LocalGreenstone.getBinOSDirectoryPath(Gatherer.client_operating_system)+"wgetrc";
321 // Issue discovered on Windows: If PATH is not made available to perl, so that wget or something else needed by
322 // WgetDownload.pm's open3() call is not on the PATH, then the perl open() call to run wget fails.
323 // So make PATH available to get open3() working:
324 env[3] = "PATH="+System.getenv("PATH");
325
326 int next_index = 4;
327 String proxy_url = proxy_urls.getProperty("HTTP");
328 if(proxy_url != null) {
329 env[next_index] = "http_proxy="+proxy_url;
330 next_index++;
331 }
332 proxy_url = proxy_urls.getProperty("HTTPS");
333 if(proxy_url != null) {
334 env[next_index] = "https_proxy="+proxy_url;
335 next_index++;
336 }
337 proxy_url = proxy_urls.getProperty("FTP");
338 if(proxy_url != null) {
339 env[next_index] = "ftp_proxy="+proxy_url;
340 next_index++;
341 }
342
343 prcs = rt.exec(cmd, env);
344 }
345 else if(Gatherer.isGsdlRemote && Gatherer.isDownloadEnabled && !Utility.isWindows()) {
346 // Not Windows, but running client with download panel
347 // Need to manually specify GSDLHOME and GSDLOS
348 env = new String[3];
349 env[0] = "GSDLHOME=" + Configuration.gsdl_path;
350 env[1] = "GSDLOS=" + Gatherer.client_operating_system;
351 env[2] = "WGETRC=" + LocalGreenstone.getBinOSDirectoryPath(Gatherer.client_operating_system)+"wgetrc"; // teach it where the wgetrc file lives, in gs2build/bin/<os>
352 prcs = rt.exec(cmd, env);
353 }
354 else {
355 // Will inherit the GLI's environment, with GSDLHOME and GSDLOS set
356 prcs = rt.exec(cmd);
357 }
358 //System.out.println(newcmd);
359
360 // Can use the following if debugging WgetDownload.pm - Reads debug stmts from the perl process' STDIN stream
361 //(new PerlReaderThread(prcs)).start();
362
363 InputStream is = prcs.getInputStream();
364 BufferedReader reader = new BufferedReader(new InputStreamReader(is));
365
366 // To be able to stop Wget, we use sockets to communicate with the perl process that launched wget
367 if (mode.equals("Web") || mode.equals("MediaWiki")) { // wget download modes other than OAI
368
369 // Need to find an available (unused) port within the range we're looking for to pass it
370 // the Perl child process, so that it may set up a listening ServerSocket at that port number
371 try {
372 boolean foundFreePort = false;
373 for(int i = 0; i < PORT_BLOCK_SIZE; i++) {
374
375 if(isPortAvailable(nextFreePort)) {
376 foundFreePort = true;
377 break;
378
379 } else {
380 incrementNextFreePort();
381 }
382 }
383
384 if(foundFreePort) {
385 // Free port number currently found becomes the port number of the socket that this
386 // DownloadJob instance will be connecting to when the user wants to prematurely stop Wget.
387 this.port = nextFreePort;
388 incrementNextFreePort();
389
390 } else {
391 throw new Exception("Cannot find an available port in the range "
392 + PORT_BASE + "-" + (PORT_BASE+PORT_BLOCK_SIZE)
393 + "\nwhich is necessary for forcibly terminating wget.");
394 }
395
396 // Communicate the chosen port for this DownloadJob instance to the perl process, so
397 // that it can set up a ServerSocket at that port to listen for any signal to terminate wget
398 OutputStream os = prcs.getOutputStream();
399 String p = ""+this.port+"\n";
400 System.err.println("Portnumber found: " + p);
401
402 os.write(p.getBytes());
403 os.close();
404
405 } catch(Exception ex) {
406 System.err.println("Sent available portnumber " + this.port + " to process' outputstream.\nBut got exception: " + ex);
407 }
408 }
409
410 BufferedReader br = new BufferedReader(new InputStreamReader(prcs.getErrorStream()));
411 // Capture the standard error stream and search for two particular occurrences.
412 String line="";
413 boolean ignore_for_robots = false;
414 int max_download = DownloadJob.UNKNOWN_MAX;
415
416 while ((line = br.readLine()) != null && !line.trim().equals("<<Finished>>") && state != STOPPED) {
417 if ( max_download == DownloadJob.UNKNOWN_MAX) {
418 if(line.lastIndexOf("<<Defined Maximum>>") != -1) {
419 max_download = DownloadJob.DEFINED_MAX;
420 }
421 else if (line.lastIndexOf("<<Undefined Maximum>>") != -1) {
422 max_download = DownloadJob.UNDEFINED_MAX;
423 }
424 }
425 else if(max_download == DownloadJob.UNDEFINED_MAX) {
426 DebugStream.println(line);
427 download_log.appendLine(line);
428 // The first magic special test is to see if we've just
429 // asked for the robots.txt file. If so we ignore
430 // the next add and then the next complete/error.
431 if(line.lastIndexOf("robots.txt;") != -1) {
432 DebugStream.println("***** Requesting robot.txt");
433 ignore_for_robots = true;
434 }
435 // If line contains "=> `" display text as the
436 // currently downloading url. Unique to add download.
437 else if(line.lastIndexOf("=> `") != -1) {
438 if(!ignore_for_robots) {
439 // Add download
440 String new_url = line.substring(line.indexOf("`") + 1, line.lastIndexOf("'"));
441 progress.addDownload("file"); //addDownload("http:/" + new_url.substring(cachedir_prefix_length()-1));
442 }
443 }
444 // If line contains "/s) - `" set currently
445 // downloading url to "Download Complete".
446 else if(line.lastIndexOf("/s) - `") != -1) {
447 String current_file_downloading = line.substring(line.indexOf("`") + 1, line.lastIndexOf("'"));
448 if(!ignore_for_robots) {
449 DebugStream.println("Not ignore for robots");
450 // Download complete
451 downloadComplete(current_file_downloading);
452 }
453 else {
454 DebugStream.println("Ignore for robots");
455 ignore_for_robots = false;
456 }
457 }
458 // The already there line begins "File `..." However this
459 // is only true in english, so instead I looked and there
460 // are few (if any at all) other messages than those above
461 // and not overwriting messages that use " `" so we'll
462 // look for that. Note this method is not guarenteed to be
463 // unique like the previous two.
464 else if(line.lastIndexOf(" `") != -1) {
465 // Not Overwriting
466 DebugStream.println("Already there.");
467 String new_url =
468 line.substring(line.indexOf("`") + 1, line.lastIndexOf("'"));
469 progress.addDownload("file"); //addDownload("http:/" + new_url.substring(cachedir_prefix_length()-1));
470 downloadWarning();
471 }
472 // Any other important message starts with the time in the form hh:mm:ss
473 else if(line.length() > 7) {
474 if(line.charAt(2) == ':' && line.charAt(5) == ':') {
475 if(!ignore_for_robots) {
476 DebugStream.println("Error.");
477 downloadFailed();
478 }
479 else {
480 ignore_for_robots = false;
481 }
482 }
483 }
484 }
485 else if (max_download == DownloadJob.DEFINED_MAX) {
486 if (line.lastIndexOf("<<Total number of record(s):") != -1) {
487 String total_ID = line.substring(line.indexOf(":") + 1, line.indexOf(">"));
488 progress.setTotalDownload((Integer.valueOf(total_ID)).intValue());
489 progress.resetFileCount();
490 progress.addDownload("files"); // for display: "Downloading files"
491 }
492 else if (line.lastIndexOf("<<Done>>") != -1) {
493 progress.increaseFileCount();
494 }
495 else if(line.lastIndexOf("<<Done:") != -1) {
496 String completed_amount = line.substring(line.indexOf(":") + 1, line.indexOf(">"));
497 progress.increaseFileCount((Integer.valueOf(completed_amount)).intValue());
498 }
499
500 DebugStream.println(line);
501 download_log.appendLine(line);
502 }
503 else {
504 System.out.println("Error!!");
505 System.exit(-1);
506 }
507 }
508
509 if(state == STOPPED) {
510 boolean terminatePerlScript = true;
511
512 // When GLI is working with wget-based download modes other than OAI (MediaWiki and Web
513 // download) and the STOP button has been pressed, wget needs to be prematurely terminated.
514 // Only wget download modes Web and MediaWiki require the use of sockets to communicate
515 // with the perl script in order to get wget to terminate. Other download modes, including
516 // wgetdownload mode OAI, can terminate in the traditional manner: close process inputstream
517 // and kill perl process. OAI launches many wgets. So that when the perl process is terminated,
518 // the currently running wget will finish off but other wgets are no longer launched.
519 if(prcs != null && (mode.equals("Web") || mode.equals("MediaWiki"))) {
520
521 // create a socket to the perl child process and communicate the STOP message
522 Socket clientSocket = null;
523 if(clientSocket == null) {
524 try {
525 clientSocket = new Socket("localhost", this.port); // connect to the port chosen for this DownloadJob instance
526
527 BufferedReader clientReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
528 String response = clientReader.readLine(); // see if we've been connected
529 System.err.println("Communicating with perl download script on port " + this.port
530 + "\nGot response from perl: " + response);
531
532 // Send the STOP signal
533 OutputStream os = clientSocket.getOutputStream();
534 String message = "<<STOP>>\n";
535 os.write(message.getBytes());
536 response = clientReader.readLine(); // see whether the stop signal has been received
537 System.err.println("GLI sent STOP signal to perl to terminate wget."
538 + "\nGot response from perl: " + response);
539
540 response = clientReader.readLine(); // see whether the perl script is ready to be terminated
541 System.err.println("Got another response from perl: " + response);
542 os.close();
543
544 clientReader.close();
545 clientSocket.close(); // close the clientSocket (the Perl end will close the server socket that Perl opened)
546 clientReader = null;
547 clientSocket = null;
548
549 if(response == null) {
550 terminatePerlScript = false;
551 }
552 } catch(IOException ex) {
553 System.err.println("Tried to communicate through client socket - port " + this.port + ", but got exception: " + ex);
554 } catch(Exception ex) {
555 System.err.println("Tried to open client socket, but got exception: " + ex);
556 }
557 }
558 }
559
560 //prcs.getInputStream().close();
561 prcs.getErrorStream().close();
562 br.close();
563 br = null;
564 if(terminatePerlScript) {
565 prcs.destroy(); // This doesn't always work, but it's worth a try
566 prcs = null;
567 }
568
569 // Notify the DownloadScrollPane which is waiting on this job to complete that we are ready
570 synchronized(this) {
571 this.notify();
572 }
573 }
574 }
575 catch (Exception ioe) {
576 //message(Utility.ERROR, ioe.toString());
577 //JTest
578 DebugStream.printStackTrace(ioe);
579 }
580 // If we've got to here and the state isn't STOPPED then the
581 // job is complete.
582 if(state == DownloadJob.RUNNING) {
583 progress.mirrorComplete();
584 previous_state = state;
585 state = DownloadJob.COMPLETE;
586 }
587 // refresh the workspace tree
588 Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.DOWNLOADED_FILES_CHANGED);
589 }
590
591 public void callDownload() {
592
593 ArrayList command_list= new ArrayList();
594
595 // the following also works for client-gli if downloading is enabled (when there's a gs2build directory inside gli)
596 command_list.add(Configuration.perl_path);
597 command_list.add("-S");
598 command_list.add(LocalGreenstone.getBinScriptDirectoryPath()+"downloadfrom.pl");
599 command_list.add("-download_mode");
600 command_list.add(mode);
601 command_list.add("-cache_dir");
602 command_list.add(Gatherer.getGLIUserCacheDirectoryPath());
603 // For the purposes of prematurely terminating wget from GLI (which creates a socket
604 // as a communication channel between GLI and Perl), it is important to tell the script
605 // that we're running as GLI. Because when running from the command prompt, it should
606 // not create this socket and do the related processing.
607 command_list.add("-gli");
608
609 ArrayList all_arg = download.getArguments(true,false);
610 for(int i = 0; i < all_arg.size(); i++) {
611 Argument argument = (Argument) all_arg.get(i);
612 if(argument.isAssigned()) {
613 command_list.add("-" + argument.getName());
614 if(argument.getType() != Argument.FLAG) {
615 command_list.add(argument.getValue());
616 }
617 }
618 }
619
620 String [] cmd = (String []) command_list.toArray(new String[0]);
621 DebugStream.println("Download job, "+command_list);
622
623 if (previous_state == DownloadJob.COMPLETE) {
624 progress.mirrorBegun(true, true);
625 }
626 else {
627 progress.mirrorBegun(false, true);
628 }
629
630 try {
631 Runtime rt = Runtime.getRuntime();
632
633 String [] env = null;
634
635 if (proxy_urls.size() != 0) {
636 // Specify proxies as environment variables
637 // Need to manually specify GSDLHOME and GSDLOS also
638 env = new String[4+proxy_urls.size()];
639
640 env[0] = "GSDLHOME=" + Configuration.gsdl_path;
641 env[1] = "GSDLOS=" + Gatherer.client_operating_system;
642 // teach it where the wgetrc file lives, in gs2build/bin/<os>:
643 env[2] = "WGETRC=" + LocalGreenstone.getBinOSDirectoryPath(Gatherer.client_operating_system)+"wgetrc";
644 // Issue discovered on Windows: If PATH is not made available to perl, so that wget or something else needed by
645 // WgetDownload.pm's open3() call is not on the PATH, then the perl open() call to run wget fails.
646 // So make PATH available to get open3() working:
647 env[3] = "PATH="+System.getenv("PATH");
648
649 int next_index = 4;
650 String proxy_url = proxy_urls.getProperty("HTTP");
651 if(proxy_url != null) {
652 env[next_index] = "http_proxy="+proxy_url;
653 next_index++;
654 }
655 proxy_url = proxy_urls.getProperty("HTTPS");
656 if(proxy_url != null) {
657 env[next_index] = "https_proxy="+proxy_url;
658 next_index++;
659 }
660 proxy_url = proxy_urls.getProperty("FTP");
661 if(proxy_url != null) {
662 env[next_index] = "ftp_proxy="+proxy_url;
663 next_index++;
664 }
665
666 prcs = new SafeProcess(cmd, env, null);
667 }
668 else if(Gatherer.isGsdlRemote && Gatherer.isDownloadEnabled && !Utility.isWindows()) {
669 // Not Windows, but running client with download panel
670 // Need to manually specify GSDLHOME and GSDLOS
671 env = new String[3];
672 env[0] = "GSDLHOME=" + Configuration.gsdl_path;
673 env[1] = "GSDLOS=" + Gatherer.client_operating_system;
674 env[2] = "WGETRC=" + LocalGreenstone.getBinOSDirectoryPath(Gatherer.client_operating_system)+"wgetrc"; // teach it where the wgetrc file lives, in gs2build/bin/<os>
675 prcs = new SafeProcess(cmd, env, null);
676 }
677 else {
678 // Will inherit the GLI's environment, with GSDLHOME and GSDLOS set
679 prcs = new SafeProcess(cmd);
680 }
681
682 //System.out.println(newcmd);
683 prcs.setMainHandler(this); // attach handler to clean up before and after process.destroy()
684 // for which DownloadJob implements SafeProcess.MainProcessHandler
685
686 // To be able to stop Wget, we use sockets to communicate with the perl process that launched wget
687 if (mode.equals("Web") || mode.equals("MediaWiki")) { // wget download modes other than OAI
688
689 // Need to find an available (unused) port within the range we're looking for to pass it
690 // the Perl child process, so that it may set up a listening ServerSocket at that port number
691 try {
692 boolean foundFreePort = false;
693 for(int i = 0; i < PORT_BLOCK_SIZE; i++) {
694
695 if(isPortAvailable(nextFreePort)) {
696 foundFreePort = true;
697 break;
698
699 } else {
700 incrementNextFreePort();
701 }
702 }
703
704 if(foundFreePort) {
705 // Free port number currently found becomes the port number of the socket that this
706 // DownloadJob instance will be connecting to when the user wants to prematurely stop Wget.
707 this.port = nextFreePort;
708 incrementNextFreePort(); //// Necessary?
709
710 } else {
711 throw new Exception("Cannot find an available port in the range "
712 + PORT_BASE + "-" + (PORT_BASE+PORT_BLOCK_SIZE)
713 + "\nwhich is necessary for forcibly terminating wget.");
714 }
715
716 // Communicate the chosen port for this DownloadJob instance to the perl process, so
717 // that it can set up a ServerSocket at that port to listen for any signal to terminate wget
718 //OutputStream os = prcs.getOutputStream();
719 String p = ""+this.port+"\n";
720 System.err.println("Portnumber found: " + p);
721
722 prcs.setInputString(p);
723
724 } catch(Exception ex) {
725 System.err.println("Sent available portnumber " + this.port + " to process' outputstream.\nBut got exception: " + ex);
726 }
727 }
728
729 ProcessErrHandler errHandler = new ProcessErrHandler(); // meaningful output comes from prcs stderr
730 ProcessOutHandler outHandler = new ProcessOutHandler(); // debugging output comes from prcs' stdout
731
732 int exitVal = prcs.runProcess(null, outHandler, errHandler);
733
734 // if prcs is interrupted (cancelled) during the blocking runProcess() call,
735 // as happens on state == STOPPED, then
736 // beforeWaitingForStreamsToEnd() is called before the process' worker threads come to a halt
737 // and afterStreamsEnded() is called when the process' worker threads have halted,
738 // beforeProcessDestroy() is called before the process is destroyed,
739 // and afterProcessDestroy() is called after the proc has been destroyed.
740 // If when beforeWaitingForStreamsEnd() stage the perl was still running but had been
741 // told to stop, then the beforeWaitingForStreamsEnd() method will make sure to communicate
742 // with the perl process over a socket and send it the termination message,
743 // which will also kill any runnning wget that perl launched.
744 // In that case, destroy() is actually called on the process at last.
745
746 }
747 catch (Exception ioe) {
748 SafeProcess.log(ioe);
749 DebugStream.printStackTrace(ioe);
750 }
751
752 // now the process is done, we can at last null it
753 prcs = null;
754
755 // If we've got to here and the state isn't STOPPED then the
756 // job is complete.
757 if(getState() == DownloadJob.RUNNING) {
758 progress.mirrorComplete();
759 previous_state = getState();
760 setState(DownloadJob.COMPLETE);
761 }
762
763 SafeProcess.log("@@@@ DONE callDownload()");
764
765 // refresh the workspace tree
766 Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.DOWNLOADED_FILES_CHANGED);
767 }
768
769 private synchronized boolean isStopped() { return state == STOPPED; }
770
771 // called when the user cancelled the download and we're told to stop both our external perl process
772 // and the wget process that it in turn launched
773 public void stopDownload() {
774 if(prcs != null) {
775 SafeProcess.log("@@@ Going to cancel the SafeProcess...");
776
777 // Whether a process ends naturally or is prematurely ended, beforeWaitingForStreamsToEnd()
778 // will be called. We've hooked this in to calling tellPerlToTerminateWget() only if the
779 // process is still running when cancel is pressed, but not when it's naturally terminated.
780 boolean hadToSendInterrupt = prcs.cancelRunningProcess(); // returns false if it was already terminating/terminated, true if interrupt sent
781
782 } else {
783 System.err.println("@@@@ No SafeProcess to cancel");
784 }
785
786 //setState(STOPPED); // would set it to stop on cancel, even if it already naturally terminated
787
788 }
789
790//*********** START of implementing interface Safeprocess.MainProcessHandler
791 // before and after processDestroy only happen when interrupted AND terminatePerlScript=true
792 public void beforeProcessDestroy() {}
793 public void afterProcessDestroy() {}
794
795 // after blocking call on closing up streamgobbler worker threads that happens
796 // upon natural termination or interruption of process' main body/thread.
797 // if not overriding, then return the parameter forciblyTerminating as-is
798 public boolean afterStreamsEnded(boolean forciblyTerminating) { return forciblyTerminating; }
799
800 // called after the SafeProcess has fully terminated (naturally or via process.destroy())
801 // and has been cleaned up
802 public void doneCleanup(boolean wasForciblyTerminated) {
803 // let the user know they can cancel again now cleanup phase is done
804 progress.enableCancelJob(true);
805
806 if(wasForciblyTerminated) {
807 setState(STOPPED); // sets it to stop only if process truly was prematurely terminated, not merely
808 // if the cancel button was clicked when it had already naturally terminated
809
810 // If the user had pressed the Close button to terminate the running job, then
811 // we're now ready to remove the display of the until now running job
812 // from the download progress bar interface
813 // But don't bother removing the progress bar if the user had only pressed the Stop button
814 if(wasClosed()) {
815 mummy.deleteCurrentDownloadJob(this);
816 }
817 }
818 }
819
820 // before blocking call of ending streamgobbler worker threads that happens
821 // after process' main body/thread has naturally terminated or been interrupted
822 public boolean beforeWaitingForStreamsToEnd(boolean forciblyTerminating) {
823 // let the user know they can't cancel during cleanup phase
824 progress.enableCancelJob(false);
825
826 SafeProcess.log("**** in beforeWaitingForStreamsToEnd()");
827
828 // state would not be STOPPED if cancel was pressed after the process naturally terminated anyway
829 // in that case we don't need to send perl the signal to terminate WGET
830 if(!forciblyTerminating) { //if(!isStopped()) {
831 SafeProcess.log("*** Process not (yet) cancelled/state not (yet) stopped");
832 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");
833 return false; // for us to be in this method at all with forciblyTerminating being false
834 // means the process is already naturally terminating, so don't unnaturally destroy it
835 }
836
837 // else the process is still running and we've been told to stop, so tell perl to stop wget first
838 // (so that process destroy can then be called thereafter)
839 return tellPerlToTerminateWget();
840 }
841//*********** END of implementing interface Safeprocess.MainProcessHandler
842
843 public boolean tellPerlToTerminateWget() {
844 SafeProcess.log("**** in tellPerlToTerminateWget()");
845
846 boolean terminatePerlScript = true;
847
848 // When GLI is working with wget-based download modes other than OAI (MediaWiki and Web
849 // download) and the STOP button has been pressed, wget needs to be prematurely terminated.
850 // Only wget download modes Web and MediaWiki require the use of sockets to communicate
851 // with the perl script in order to get wget to terminate. Other download modes, including
852 // wgetdownload mode OAI, can terminate in the traditional manner: close process inputstream
853 // and kill perl process. OAI launches many wgets. So that when the perl process is terminated,
854 // the currently running wget will finish off but other wgets are no longer launched.
855 if((mode.equals("Web") || mode.equals("MediaWiki"))) {
856 SafeProcess.log("@@@ Socket communication to end wget");
857 // create a socket to the perl child process and communicate the STOP message
858 Socket clientSocket = null;
859 BufferedReader clientReader = null;
860 OutputStream os = null;
861
862 if(clientSocket == null) {
863 try {
864 clientSocket = new Socket("localhost", this.port); // connect to the port chosen for this DownloadJob instance
865
866 clientReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
867 String response = clientReader.readLine(); // see if we've been connected
868 System.err.println("Communicating with perl download script on port " + this.port
869 + "\nGot response from perl: " + response);
870
871 // Send the STOP signal
872 os = clientSocket.getOutputStream();
873 String message = "<<STOP>>\n";
874 os.write(message.getBytes());
875 response = clientReader.readLine(); // see whether the stop signal has been received
876 System.err.println("GLI sent STOP signal to perl to terminate wget."
877 + "\nGot response from perl: " + response);
878
879 response = clientReader.readLine(); // see whether the perl script is ready to be terminated
880 System.err.println("Got another response from perl: " + response);
881
882 if(response == null) { // why? Is it because the process has already terminated naturally if response is null?
883 terminatePerlScript = false;
884 }
885 } catch(IOException ex) {
886 if(ex instanceof IOException && ex.getMessage().indexOf("Connection refused") != -1) {
887 terminatePerlScript = false; // no socket listening on other end because process ended
888 System.err.println("Tried to communicate through client socket - port " + this.port + ", but the process seems to have already ended naturally");
889 } else {
890 System.err.println("Tried to communicate through client socket - port " + this.port + ", but got exception: " + ex);
891 }
892
893 } catch(Exception ex) {
894 System.err.println("Tried to open client socket, but got exception: " + ex);
895 } finally {
896 SafeProcess.closeResource(os);
897 SafeProcess.closeResource(clientReader);
898 SafeProcess.closeSocket(clientSocket); // close the clientSocket (the Perl end will close the server socket that Perl opened)
899 os = null;
900 clientReader = null;
901 clientSocket = null;
902 }
903 }
904 }
905
906 return terminatePerlScript; // if true, it will call destroy() on the SafeProcess' process
907 }
908
909
910 /** Called by the WGet native code when the current download is
911 * completed. In turn all download listeners are informed.
912 */
913 public void downloadComplete() {
914 progress.downloadComplete(); // now this is synchronized
915 }
916
917
918 public void downloadComplete(String current_file_downloading)
919 {
920 progress.downloadComplete(); // now this is synchronized
921 DebugStream.println("Download complete: " + current_file_downloading);
922 }
923
924
925 /** Called by the WGet native code when the requested download returns
926 * a status code other than 200.
927 */
928 public void downloadFailed() {
929 // TODO!!
930 //synchronized(failed_urls) {
931 //failed_urls.add(current_url); // It is the current url that failed
932 //}
933 progress.downloadFailed(); // now this is synchronized
934 //DebugStream.println("Download failed: " + current_url);
935 }
936
937 /**
938 */
939 public void downloadWarning() {
940 progress.downloadWarning(); // now this is synchronized
941 }
942
943 public AppendLineOnlyFileDocument getLogDocument() {
944 return download_log;
945 }
946
947 /**
948 * @return Returns the progress bar associated with this job.
949 */
950 public DownloadProgressBar getProgressBar() {
951 return progress;
952 }
953
954 /** Called to discover if the user wanted this thread to run or if
955 * it is paused.
956 * @return An int representing the current DownloadJob state.
957 */
958 public synchronized int getState() {
959 return state;
960 }
961
962 /** @return true if the close button of the DownloadProgressBar was pressed,
963 * false otherwise such as if the Stop button had been pressed.
964 */
965 private synchronized boolean wasClosed() {
966 return this.wasClosed;
967 }
968
969 /** Returns the current state of the stop flag for this job.
970 * @return A boolean representing whether the user has requested to
971 * stop.
972 */
973 public synchronized boolean hasSignalledStop() {
974 if(state == DownloadJob.STOPPED || state == DownloadJob.PAUSED ||
975 state == DownloadJob.COMPLETE) {
976 return true;
977 }
978 return false;
979 }
980
981 public synchronized void setState(int state) {
982 previous_state = this.state;
983 this.state = state;
984 }
985
986 private synchronized void setClosed() {
987 this.wasClosed = true;
988 }
989
990 /** A convenience call.
991 * @return A String representing the url of the initial url (root node of the mirrored tree).
992 */
993 public String toString() {
994 return download_url;
995 }
996
997 /** Called by the WGet native code to signal the current progress of
998 * downloading.
999 * @param current A long representing the number of bytes that have
1000 * been downloaded since the last update.
1001 * @param expected A long representing the total number of bytes
1002 * expected for this download.
1003 */
1004 public void updateProgress(long current, long expected) {
1005 progress.updateProgress(current, expected);
1006 }
1007
1008
1009 /*
1010 Go through https://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html series of
1011 Java articles on concurrency again.
1012 Go through http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
1013
1014 http://stackoverflow.com/questions/574240/is-there-an-advantage-to-use-a-synchronized-method-instead-of-a-synchronized-blo
1015
1016 "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."
1017 "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."
1018
1019 "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"
1020
1021 "A private lock is a defensive mechanism, which is never a bad idea.
1022
1023 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."
1024
1025 http://stackoverflow.com/questions/8393883/is-synchronized-keyword-exception-safe
1026 "In any scoped thread-safe block, the moment you get out of it, the thread-safety is gone."
1027 "In case of an exception the lock will be released."
1028
1029 http://stackoverflow.com/questions/8259479/should-i-synchronize-listener-notifications-or-not
1030 "Use a CopyOnWriteArrayList for your listener arrays."
1031 "If you use the CopyOnWriteArrayList, then you don't have to synchronize when iterating."
1032 "CopyOnWriteArrayList is thread-safe, so there is no need to synchronize."
1033
1034 "Use a ConcurrentLinkedQueue<Listener> ... for this kind of problems: adding, removing and iterating simultaneously on a collection.
1035 A precision : this solution prevents a listener from being called from the very moment it is deregistered."
1036 "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.
1037 It's the best of both world: ensuring synchronization, while being fine grained on who gets called and who's not."
1038
1039 http://stackoverflow.com/questions/8260205/when-a-listener-is-removed-is-it-okay-that-the-event-be-called-on-that-listener
1040
1041 http://stackoverflow.com/questions/2282166/java-synchronizing-on-primitives
1042
1043 1. You can't lock on a primitive and
1044 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).
1045
1046 Cross-talk:
1047 "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."
1048 */
1049
1050
1051 // Inner thread class that reads from process downloadfrom.pl's std output stream
1052 private class ProcessOutHandler extends SafeProcess.CustomProcessHandler {
1053
1054 public ProcessOutHandler() {
1055 super(SafeProcess.STDOUT);
1056 }
1057
1058 public void run(Closeable stream) {
1059 InputStream is = (InputStream) stream;
1060 BufferedReader eReader = null;
1061 try {
1062
1063 String message = null;
1064 eReader = new BufferedReader(new InputStreamReader(is));
1065 while(!Thread.currentThread().isInterrupted() && (message = eReader.readLine()) != null) {
1066 if(!message.equals("\n")) {
1067 System.err.println("**** Perl STDOUT: " + message);
1068 }
1069 }
1070 if(Thread.currentThread().isInterrupted()) {
1071 System.err.println("**** Perl INTERRUPTed.");
1072 } else {
1073 System.err.println("**** Perl ENDed.");
1074 }
1075
1076 } catch(Exception e) {
1077 System.err.println("Thread - caught exception: " + e);
1078 } finally {
1079 if(Thread.currentThread().isInterrupted()) {
1080 SafeProcess.log("@@@ Successfully interrupted " + Thread.currentThread().getName() + ".");
1081 }
1082 SafeProcess.closeResource(eReader);
1083 eReader = null;
1084 }
1085 }
1086 }
1087
1088
1089 private class ProcessErrHandler extends SafeProcess.CustomProcessHandler {
1090
1091 public ProcessErrHandler() {
1092 super(SafeProcess.STDERR);
1093 }
1094
1095 public void run(Closeable stream) {
1096 InputStream eis = (InputStream) stream;
1097
1098 BufferedReader br = null;
1099 try {
1100 br = new BufferedReader(new InputStreamReader(eis));
1101
1102 // Capture the standard error stream and search for two particular occurrences.
1103 String line="";
1104 boolean ignore_for_robots = false;
1105 int max_download = DownloadJob.UNKNOWN_MAX;
1106
1107 // handle to outer class objects that need synchronization (on either objects or their methods)
1108 DownloadProgressBar progress = DownloadJob.this.progress;
1109 AppendLineOnlyFileDocument download_log = DownloadJob.this.download_log;
1110
1111 boolean noCheckCertificate_needed = false; // determine whether this flag needs to be set for wget (under Preferences > Connection)
1112
1113 while (!Thread.currentThread().isInterrupted() && (line = br.readLine()) != null
1114 && !line.trim().equals("<<Finished>>") /*&& !isStopped()*/) {
1115 if (max_download == DownloadJob.UNKNOWN_MAX) {
1116 if(line.lastIndexOf("<<Defined Maximum>>") != -1) {
1117 max_download = DownloadJob.DEFINED_MAX;
1118 }
1119 else if (line.lastIndexOf("<<Undefined Maximum>>") != -1) {
1120 max_download = DownloadJob.UNDEFINED_MAX;
1121 }
1122 }
1123 else if(max_download == DownloadJob.UNDEFINED_MAX) {
1124 DebugStream.println(line);
1125 download_log.appendLine(line); // now synchronized
1126 // The first magic special test is to see if we've just
1127 // asked for the robots.txt file. If so we ignore
1128 // the next add and then the next complete/error.
1129 if(line.lastIndexOf("robots.txt;") != -1) {
1130 DebugStream.println("***** Requesting robot.txt");
1131 ignore_for_robots = true;
1132 }
1133 else if(line.indexOf("--no-check-certificate") != -1) {
1134 downloadWarning();
1135 noCheckCertificate_needed = true;
1136 }
1137 // If line contains "=> `" display text as the
1138 // currently downloading url. Unique to add download.
1139 else if(line.lastIndexOf("=> `") != -1) {
1140 if(!ignore_for_robots) {
1141 // Add download
1142 String new_url = line.substring(line.indexOf("`") + 1, line.lastIndexOf("'"));
1143
1144 // now synchronized
1145 progress.addDownload("file"); //addDownload("http:/" + new_url.substring(cachedir_prefix_length()-1));
1146 }
1147 }
1148 // If line contains "/s) - `" set currently
1149 // downloading url to "Download Complete".
1150 // Currently: on windows ` marks start quote of downloaded file, but on linux ' marks it
1151 else if(line.lastIndexOf("/s) - `") != -1 || line.lastIndexOf("/s) - '") != -1) {
1152 String startChar = (line.lastIndexOf("/s) - `") != -1) ? "`" : "'";
1153 String current_file_downloading = line.substring(line.indexOf(startChar) + 1, line.lastIndexOf("'"));
1154 if(!ignore_for_robots) {
1155 DebugStream.println("Not ignore for robots");
1156 // Download complete
1157 downloadComplete(current_file_downloading); // synchronized
1158 }
1159 else {
1160 DebugStream.println("Ignore for robots");
1161 ignore_for_robots = false;
1162 }
1163 }
1164 // The already there line begins "File `..." However this
1165 // is only true in english, so instead I looked and there
1166 // are few (if any at all) other messages than those above
1167 // and not overwriting messages that use " `" so we'll
1168 // look for that. Note this method is not guarenteed to be
1169 // unique like the previous two.
1170 else if(line.lastIndexOf(" `") != -1) {
1171 // Not Overwriting
1172 DebugStream.println("Already there.");
1173 String new_url = line.substring(line.indexOf("`") + 1, line.lastIndexOf("'"));
1174
1175 progress.addDownload("file"); //addDownload("http:/" + new_url.substring(cachedir_prefix_length()-1));
1176 downloadWarning();
1177 }
1178 // Any other important message starts with the time in the form hh:mm:ss
1179 else if(line.length() > 7) {
1180 if(line.charAt(2) == ':' && line.charAt(5) == ':') {
1181 if(!ignore_for_robots) {
1182 DebugStream.println("Error.");
1183 downloadFailed();
1184 }
1185 else {
1186 ignore_for_robots = false;
1187 }
1188 }
1189 }
1190 }
1191 else if (max_download == DownloadJob.DEFINED_MAX) {
1192 if (line.lastIndexOf("<<Total number of record(s):") != -1) {
1193 String total_ID = line.substring(line.indexOf(":") + 1, line.indexOf(">"));
1194
1195 progress.setTotalDownload((Integer.valueOf(total_ID)).intValue());
1196 progress.resetFileCount();
1197 progress.addDownload("files"); // for display: "Downloading files"
1198
1199 }
1200 else if (line.lastIndexOf("<<Done>>") != -1) {
1201 progress.increaseFileCount();
1202 }
1203 else if(line.lastIndexOf("<<Done:") != -1) {
1204 String completed_amount = line.substring(line.indexOf(":") + 1, line.indexOf(">"));
1205 progress.increaseFileCount((Integer.valueOf(completed_amount)).intValue());
1206 }
1207
1208 DebugStream.println(line);
1209 download_log.appendLine(line);
1210 }
1211 else {
1212 System.out.println("Error!!");
1213 System.exit(-1);
1214 }
1215 }
1216 if(noCheckCertificate_needed) { // can't download from (some nested) URL as its
1217 // certificate can't be verified. User must turn on --no-check-certificate
1218 // if they want to download from there anyway, but it will do so insecurely
1219 download_log.appendLine("************");
1220 download_log.appendLine(Dictionary.get("Mirroring.DownloadJob.Warning_No_Valid_Certificate"));
1221 download_log.appendLine(Dictionary.get("Mirroring.DownloadJob.Enable_NoCheckCertificate"));
1222 download_log.appendLine("************");
1223 }
1224
1225 } catch (IOException ioe) {
1226 //message(Utility.ERROR, ioe.toString());
1227 //JTest
1228 DebugStream.printStackTrace(ioe);
1229
1230 } finally {
1231 if(Thread.currentThread().isInterrupted()) { // if the thread this class is running in is interrupted
1232 SafeProcess.log("@@@ Successfully interrupted " + Thread.currentThread().getName() + ".");
1233 }
1234
1235 SafeProcess.closeResource(br);
1236 br = null;
1237 }
1238
1239 }
1240 }
1241}
Note: See TracBrowser for help on using the repository browser.