001    package org.hackystat.sensorshell.command;
002    
003    import java.util.Date;
004    
005    import org.hackystat.sensorbase.client.SensorBaseClient;
006    import org.hackystat.sensorshell.SensorShellProperties;
007    import org.hackystat.sensorshell.SingleSensorShell;
008    
009    /**
010     * Implements the Ping command, which ensures that the SensorBase is reachable. 
011     * @author Philip Johnson
012     */
013    public class PingCommand extends Command {
014      
015      /** The timeout in milliseconds, initialized from sensorshell.properties.*/
016      private int timeout;
017      
018      /**
019       * Creates the PingCommand. 
020       * @param shell The sensorshell. 
021       * @param properties The sensorproperties. 
022       */
023      public PingCommand(SingleSensorShell shell, SensorShellProperties properties) {
024        super(shell, properties);
025        this.timeout = properties.getPingTimeout() * 1000;
026      }
027      
028      /**
029       * Does a ping on the hackystat server and returns true if the server was
030       * accessible. A ping-able server indicates the data will be sent to it,
031       * while a non-pingable server indicates that data will be stored offline.
032       *
033       * @return   True if the server could be pinged.
034       */
035      public boolean isPingable() {
036        return this.isPingable(this.timeout);
037      }
038    
039      /**
040       * Does a ping on the hackystat server and returns true if the server was
041       * accessible. A ping-able server indicates the data will be sent to it,
042       * while a non-pingable server indicates that data will be stored offline.
043       * If the server is not reachable, or does not respond with given time frame, false will
044       * be returned.
045       *
046       * @param timeout Maximum seconds to wait for server response. A 0 value or negative
047       * value is equivalent to set time out to infinity.
048       * @return   True if the server could be pinged.
049       */
050      public boolean isPingable(int timeout) {
051        boolean result = false;
052        long startTime = new Date().getTime();
053        PingWorkerThread workThread = new PingWorkerThread(this.host, this.email, this.password);
054        workThread.start();
055        try {
056          workThread.join(timeout);  //block this thread until work thread dies or times out.
057        }
058        catch (InterruptedException ex) {
059          //do nothing
060        }
061        //if work thread is still alive, then it's time out, result = false by default.
062        if (!workThread.isAlive()) {
063          result = workThread.serverPingable;
064        }
065        long elapsedTime = new Date().getTime() - startTime;
066        this.shell.println("Pinged " + this.host + " in " + elapsedTime + " ms. Result is: " + result);
067        return result;
068      }
069    
070      /**
071       * Worker thread to ping the server to determine whether it's reachable or not. The original
072       * ping command is implemented as a synchronous command, a separate thread is need to
073       * implement time out feature.
074       *
075       * @author Qin ZHANG
076       */
077      private static class PingWorkerThread extends Thread {
078    
079        /**
080         * This instance will only be be accessed in the parent thread after the termination of this
081         * thread, there is no need to synchronize access.
082         */
083        private boolean serverPingable = false;
084        private String host;
085        private String email;
086        private String password;
087    
088        /**
089         * Constructs this worker thread.
090         * @param host The sensorbase host.
091         * @param email The client email.
092         * @param password The client password.
093         */
094        public PingWorkerThread(String host, String email, String password) {
095          setDaemon(true); //want VM to terminate even if this thread is alive.
096          this.host = host;
097          this.email = email;
098          this.password = password;
099        }
100    
101        /** Pings the server synchronously. */
102        @Override
103        public void run() {
104          this.serverPingable = SensorBaseClient.isRegistered(this.host, this.email, this.password);
105        }
106      }
107    
108    }