Changeset 17525


Ignore:
Timestamp:
2008-10-13T21:40:31+13:00 (16 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

File:
1 edited

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}
Note: See TracChangeset for help on using the changeset viewer.