Changeset 17525

Show
Ignore:
Timestamp:
13.10.2008 21:40:31 (11 years ago)
Author:
ak19
Message:

Added code for telling the perl script to forcibly terminate wget when the user closes a download before it is true. Communication with the perl script is through a socket (for each download) since using inputstreams end up blocking on the perl side and the SIGTERM sent upon process.destroy() can't be handled on Windows, and using per;IOSelect with file

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • gli/trunk/src/org/greenstone/gatherer/download/DownloadJob.java

    r13594 r17525  
    7676 
    7777    private String download_url = ""; 
    78  
     78     
    7979    //    private String current_url; 
    8080    //    private String destination; 
     
    9696    public static int DEFINED_MAX  = 1; 
    9797    public static int UNDEFINED_MAX  = 2; 
    98  
     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     
    99106    private String mode = null; 
    100107    
    101108    private String proxy_url; 
    102  
     109     
    103110    /** 
    104111     */ 
     
    169176    // The stop_start_button is used to alternately start or stop the 
    170177    // job. If the current state of the job is paused then this 
    171     // restart is logically equivelent to a resume. 
     178    // restart is logically equivalent to a resume. 
    172179    if(event.getSource() == progress.stop_start_button) { 
    173180        previous_state = state; 
     
    189196    } 
    190197 
     198    /** Given a portnumber to check, returns true if it is available 
     199     * (if nothing's listening there already). */ 
     200    public static boolean isPortAvailable(int portnum) { 
     201    Socket tmpSocket = null; 
     202    try { 
     203        tmpSocket = new Socket("localhost", portnum); 
     204        tmpSocket.close(); 
     205        return false; 
     206 
     207    } catch(ConnectException ex){ 
     208        // "Signals that an error occurred while attempting to connect a socket  
     209        // to a remote address and port. Typically, the connection was refused 
     210        // remotely (e.g., no process is listening on the remote address/port)." 
     211        System.err.println("Port " + portnum + " not yet in use."); 
     212        tmpSocket = null; 
     213        return true; 
     214 
     215    } catch(Exception ex) {  
     216        // includes BindException "Signals that an error occurred while attempting 
     217        // to bind a socket to a local address and port. Typically, the port is in 
     218        // use, or the requested local address could not be assigned." 
     219        tmpSocket = null; 
     220        return false; 
     221    } 
     222    } 
     223 
     224    /** Circular buffer. Modifies the value of nextFreePort (the buffer index). */ 
     225    private void incrementNextFreePort() { 
     226    int offset = nextFreePort - PORT_BASE; 
     227    offset = (offset + 1) % PORT_BLOCK_SIZE; 
     228    nextFreePort = PORT_BASE + offset; 
     229    }  
    191230 
    192231    public void callDownload() { 
     
    202241    command_list.add("-cache_dir"); 
    203242    command_list.add(Gatherer.getGLIUserCacheDirectoryPath()); 
     243    // For the purposes of prematurely terminating wget from GLI (which creates a socket 
     244    // as a communication channel between GLI and Perl), it is important to tell the script 
     245    // that we're running as GLI. Because when running from the command prompt, it should 
     246    // not create this socket and do the related processing. 
     247    command_list.add("-gli"); 
    204248     
    205249    ArrayList all_arg = download.getArguments(true,false); 
     
    252296        } 
    253297        } 
    254   
    255298        //System.out.println(newcmd);        
    256299 
    257         InputStreamReader isr = new InputStreamReader(prcs.getErrorStream()); 
    258         BufferedReader br = new BufferedReader(isr); 
    259         // Capture the standard error stream and seach for two particular occurances. 
     300        // Can use the following if debugging WgetDownload.pm - Reads debug stmts from the perl process' STDIN stream 
     301        //(new PerlReaderThread(prcs)).start(); 
     302 
     303        InputStream is = prcs.getInputStream(); 
     304        BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 
     305 
     306        // To be able to stop Wget, we use sockets to communicate with the perl process that launched wget 
     307        if (!mode.equals("Z3950") && !mode.equals("SRW")) { // wget-based Download modes (OAI, MediaWiki and Web download) 
     308         
     309        // Need to find an available (unused) port within the range we're looking for to pass it 
     310        // the Perl child process, so that it may set up a listening ServerSocket at that port number 
     311        try { 
     312            boolean foundFreePort = false; 
     313            for(int i = 0; i < PORT_BLOCK_SIZE; i++) { 
     314 
     315            if(isPortAvailable(nextFreePort)) { 
     316                foundFreePort = true; 
     317                break; 
     318                 
     319            } else { 
     320                incrementNextFreePort(); 
     321            } 
     322            } 
     323             
     324            if(foundFreePort) { 
     325            // Free port number currently found becomes the port number of the socket that this 
     326            // DownloadJob instance will be connecting to when the user wants to prematurely stop Wget. 
     327            this.port = nextFreePort; 
     328            incrementNextFreePort(); 
     329             
     330            } else { 
     331            throw new Exception("Cannot find an available port in the range "  
     332                        + PORT_BASE + "-" + (PORT_BASE+PORT_BLOCK_SIZE) 
     333                        + "\nwhich is necessary for forcibly terminating wget."); 
     334            } 
     335 
     336            // Communicate the chosen port for this DownloadJob instance to the perl process, so 
     337            // that it can set up a ServerSocket at that port to listen for any signal to terminate wget 
     338            OutputStream os = prcs.getOutputStream(); 
     339            String p = ""+this.port+"\n"; 
     340            System.err.println("Portnumber found: " + p); 
     341 
     342            os.write(p.getBytes()); 
     343            os.close(); 
     344           
     345        } catch(Exception ex) { 
     346            System.err.println("Sent available portnumber " + this.port + " to process' outputstream.\nBut got exception: " + ex); 
     347        } 
     348        } 
     349         
     350        BufferedReader br = new BufferedReader(new InputStreamReader(prcs.getErrorStream())); 
     351        // Capture the standard error stream and search for two particular occurrences. 
    260352        String line=""; 
    261353        boolean ignore_for_robots = false; 
    262354        int max_download = DownloadJob.UNKNOWN_MAX; 
    263  
    264  
     355        
    265356        while ((line = br.readLine()) != null && !line.trim().equals("<<Finished>>") && state != STOPPED) { 
    266  
    267357        if ( max_download == DownloadJob.UNKNOWN_MAX) { 
    268358            if(line.lastIndexOf("<<Defined Maximum>>") != -1) { 
     
    356446        } 
    357447        if(state == STOPPED) { 
    358         isr.close(); 
    359         prcs.destroy(); // This doesn't always work, but it's worth a try 
     448        // When GLI is working with wget-based download modes (OAI, MediaWiki and Web download) 
     449        // and the STOP button has been pressed, wget needs to be prematurely terminated. 
     450        // The presence of the tmpfile will indicate to the perl script that it's time to kill wget. 
     451        if(prcs != null && !mode.equals("Z3950") && !mode.equals("SRW")) {  
     452             
     453            // create a socket to the perl child process and communicate the STOP message 
     454            Socket clientSocket = null; 
     455            if(clientSocket == null) { 
     456            try { 
     457                clientSocket = new Socket("localhost", this.port); // connect to the port chosen for this DownloadJob instance 
     458                 
     459                BufferedReader clientReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 
     460                String response = clientReader.readLine(); // see if we've been connected 
     461                System.err.println("Communicating with perl download script on port " + this.port  
     462                           + "\nGot response from perl: " + response);  
     463                 
     464                // Send the STOP signal 
     465                OutputStream os = clientSocket.getOutputStream(); 
     466                String message = "<<STOP>>\n"; 
     467                os.write(message.getBytes()); 
     468                response = clientReader.readLine(); // see whether the stop signal has been received 
     469                System.err.println("GLI sent STOP signal to perl to terminate wget."  
     470                           + "\nGot response from perl: " + response); 
     471                os.close(); 
     472                 
     473                clientReader.close(); 
     474                clientSocket.close(); // close the clientSocket (the Perl end will close the server socket that Perl opened) 
     475                clientReader = null; 
     476                clientSocket = null; 
     477            } catch(IOException ex) { 
     478                System.err.println("Tried to communicate through client socket - port " + this.port + ", but got exception: " + ex); 
     479            } catch(Exception ex) { 
     480                System.err.println("Tried to open client socket, but got exception: " + ex); 
     481            } 
     482            } 
     483        } 
     484         
     485        //prcs.getInputStream().close(); 
     486        prcs.getErrorStream().close(); 
     487        prcs.destroy(); // This doesn't always work, but it's worth a try        
     488        prcs = null; 
     489        br.close(); 
     490        br = null; 
     491 
     492        // Notify the DownloadScrollPane which is waiting on this job to complete that we are ready  
     493        synchronized(this) { 
     494            this.notify(); 
     495        } 
    360496        } 
    361         
    362497    } 
    363498    catch (Exception ioe) { 
     
    372507        previous_state = state; 
    373508        state = DownloadJob.COMPLETE; 
    374          
    375509    } 
    376510    // refresh the workspace tree 
    377511    Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.DOWNLOADED_FILES_CHANGED); 
    378         
    379512    } 
    380513 
     
    464597    progress.updateProgress(current, expected); 
    465598    } 
     599 
     600     
     601    // Inner thread class that reads from process downloadfrom.pl's errorstream 
     602    private class PerlReaderThread extends Thread { 
     603    Process prcs = null; 
     604 
     605    public PerlReaderThread(Process proc) { 
     606        this.prcs = proc; 
     607    } 
     608 
     609    public void run() { 
     610        try { 
     611        if(prcs != null) { 
     612            String message = null; 
     613            BufferedReader eReader = new BufferedReader(new InputStreamReader(prcs.getInputStream())); 
     614            while(prcs != null && (message = eReader.readLine()) != null) { 
     615            System.err.println("**** Perl STDOUT: " + message); 
     616            } 
     617             
     618            if(prcs != null && eReader != null) { 
     619            eReader.close(); 
     620            eReader = null; 
     621            System.err.println("**** Perl ENDed."); 
     622            } 
     623        } 
     624        } catch(Exception e) { 
     625        System.err.println("Thread - caught exception: " + e); 
     626        } 
     627    } 
     628    } 
    466629}