2011/08/05 - Jakarta Cactus has been retired.

For more information, please explore the Attic.

Clover coverage report - Cactus 1.8dev for J2EE API 1.3
Coverage timestamp: Sun Mar 26 2006 18:50:18 BRT
file stats: LOC: 407   Methods: 16
NCLOC: 196   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
ContainerRunner.java 0% 0% 0% 0%
coverage
 1   
 /* 
 2   
  * ========================================================================
 3   
  * 
 4   
  * Copyright 2003-2005 The Apache Software Foundation.
 5   
  *
 6   
  * Licensed under the Apache License, Version 2.0 (the "License");
 7   
  * you may not use this file except in compliance with the License.
 8   
  * You may obtain a copy of the License at
 9   
  * 
 10   
  *   http://www.apache.org/licenses/LICENSE-2.0
 11   
  * 
 12   
  * Unless required by applicable law or agreed to in writing, software
 13   
  * distributed under the License is distributed on an "AS IS" BASIS,
 14   
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15   
  * See the License for the specific language governing permissions and
 16   
  * limitations under the License.
 17   
  * 
 18   
  * ========================================================================
 19   
  */
 20   
 package org.apache.cactus.integration.ant.container;
 21   
 
 22   
 import java.io.IOException;
 23   
 import java.io.InputStream;
 24   
 import java.net.HttpURLConnection;
 25   
 import java.net.URL;
 26   
 
 27   
 import org.apache.cactus.integration.ant.util.AntLog;
 28   
 import org.apache.commons.logging.Log;
 29   
 import org.apache.tools.ant.BuildException;
 30   
 
 31   
 /**
 32   
  * Support class that handles the lifecycle of a container, which basically
 33   
  * consists of startup and shutdown.
 34   
  * 
 35   
  * @version $Id: ContainerRunner.java 239130 2005-01-29 15:49:18Z vmassol $
 36   
  */
 37   
 public final class ContainerRunner
 38   
 {
 39   
     // Instance Variables ------------------------------------------------------
 40   
 
 41   
     /**
 42   
      * The container to run.
 43   
      */
 44   
     private Container container;
 45   
 
 46   
     /**
 47   
      * The URL that is continuously pinged to check if the container is running.
 48   
      */
 49   
     private URL testURL;
 50   
 
 51   
     /**
 52   
      * Timeout in milliseconds after which to give up connecting to the
 53   
      * container.
 54   
      */
 55   
     private long timeout = 180000;
 56   
 
 57   
     /**
 58   
      * The time interval in milliseconds to sleep between polling the container.
 59   
      */
 60   
     private long checkInterval = 500;
 61   
 
 62   
     /**
 63   
      * The time to sleep after the container has shut down. 
 64   
      */
 65   
     private long shutDownWait = 2000;
 66   
 
 67   
     /**
 68   
      * Whether the container had already been running before.
 69   
      */
 70   
     private boolean alreadyRunning;
 71   
 
 72   
     /**
 73   
      * The server name as returned in the 'Server' header of the server's
 74   
      * HTTP response.
 75   
      */
 76   
     private String serverName;
 77   
 
 78   
     /**
 79   
      * The log to use.
 80   
      */
 81   
     private transient Log log = AntLog.NULL;
 82   
 
 83   
     // Constructors ------------------------------------------------------------
 84   
 
 85   
     /**
 86   
      * Constructor.
 87   
      * 
 88   
      * @param theContainer The container to run
 89   
      */
 90  0
     public ContainerRunner(Container theContainer)
 91   
     {
 92  0
         this.container = theContainer;
 93   
     }
 94   
 
 95   
     // Public Methods ----------------------------------------------------------
 96   
 
 97   
     /**
 98   
      * Returns the server name as reported in the 'Server' header of HTTP 
 99   
      * responses from the server.
 100   
      * 
 101   
      * @return The server name
 102   
      */
 103  0
     public String getServerName()
 104   
     {
 105  0
         return this.serverName;
 106   
     }
 107   
 
 108   
     /**
 109   
      * Method called by the task to perform the startup of the container. This
 110   
      * method takes care of starting the container in another thread, and
 111   
      * polling the test URL to check whether startup has completed. As soon as
 112   
      * the URL is available (or the timeout is exceeded), control is returned to
 113   
      * the caller.
 114   
      * 
 115   
      * @throws IllegalStateException If the 'url' property is <code>null</code>
 116   
      */
 117  0
     public void startUpContainer() throws IllegalStateException
 118   
     {
 119  0
         if (this.testURL == null)
 120   
         {
 121  0
             throw new IllegalStateException("Property [url] must be set");
 122   
         }
 123   
 
 124   
         // Try connecting in case the server is already running. If so, does
 125   
         // nothing
 126  0
         this.alreadyRunning = isAvailable(testConnectivity(this.testURL));
 127  0
         if (this.alreadyRunning)
 128   
         {
 129   
             // Server is already running. Record this information so that we
 130   
             // don't stop it afterwards.
 131  0
             this.log.debug("Server is already running");
 132  0
             return;
 133   
         }
 134   
 
 135   
         // Now start the server in another thread
 136  0
         Thread thread = new Thread(new Runnable()
 137   
         {
 138  0
             public void run()
 139   
             {
 140  0
                 container.startUp();
 141   
             }
 142   
         });
 143  0
         thread.start();
 144   
 
 145   
         // Continuously try calling the test URL until it succeeds or
 146   
         // until a timeout is reached (we then throw a build exception).
 147  0
         long startTime = System.currentTimeMillis();
 148  0
         int responseCode = -1;
 149  0
         do
 150   
         {
 151  0
             if ((System.currentTimeMillis() - startTime) > this.timeout)
 152   
             {
 153  0
                 throw new BuildException("Failed to start the container after "
 154   
                     + "more than [" + this.timeout + "] ms. Trying to connect "
 155   
                     + "to the [" + this.testURL + "] test URL yielded a ["
 156   
                     + responseCode + "] error code. Please run in debug mode "
 157   
                     + "for more details about the error.");
 158   
             }
 159  0
             sleep(this.checkInterval);
 160  0
             this.log.debug("Checking if server is up ...");
 161  0
             responseCode = testConnectivity(this.testURL);
 162  0
         } while (!isAvailable(responseCode));
 163   
 
 164   
         // Wait a few ms more (just to be sure !)
 165  0
         sleep(this.container.getStartUpWait());
 166   
 
 167  0
         this.serverName = retrieveServerName(this.testURL);
 168  0
         this.log.trace("Server [" + this.serverName + "] started");
 169   
     }
 170   
 
 171   
     /**
 172   
      * Method called by the task to perform the stopping of the container. This
 173   
      * method takes care of stopping the container in another thread, and
 174   
      * polling the test URL to check whether shutdown has completed. As soon as
 175   
      * the URL stops responding, control is returned to the caller.
 176   
      * 
 177   
      * @throws IllegalStateException If the 'url' property is <code>null</code>
 178   
      */
 179  0
     public void shutDownContainer() throws IllegalStateException
 180   
     {
 181  0
         if (this.testURL == null)
 182   
         {
 183  0
             throw new IllegalStateException("Property [url] must be set");
 184   
         }
 185   
 
 186   
         // Don't shut down a container that has not been started by us
 187  0
         if (this.alreadyRunning)
 188   
         {
 189  0
             return;
 190   
         }
 191   
         
 192  0
         if (!isAvailable(testConnectivity(this.testURL)))
 193   
         {
 194  0
             this.log.debug("Server isn't running!");
 195  0
             return;
 196   
         }
 197   
 
 198   
         // Call the target that stops the server, in another thread. The called
 199   
         // target must be blocking.
 200  0
         Thread thread = new Thread(new Runnable()
 201   
         {
 202  0
             public void run()
 203   
             {
 204  0
                 container.shutDown();
 205   
             }
 206   
         });
 207  0
         thread.start();
 208   
 
 209   
         // Continuously try calling the test URL until it fails
 210  0
         do 
 211   
         {
 212  0
             sleep(this.checkInterval);
 213  0
         } while (isAvailable(testConnectivity(this.testURL)));
 214   
 
 215   
         // sleep a bit longer to be sure the container has terminated
 216  0
         sleep(this.shutDownWait);
 217   
         
 218  0
         this.log.debug("Server stopped!");
 219   
     }
 220   
 
 221   
     /**
 222   
      * Sets the time interval to sleep between polling the container. 
 223   
      * 
 224   
      * The default interval is 500 milliseconds.
 225   
      * 
 226   
      * @param theCheckInterval The interval in milliseconds
 227   
      */
 228  0
     public void setCheckInterval(long theCheckInterval)
 229   
     {
 230  0
         this.checkInterval = theCheckInterval;
 231   
     }
 232   
 
 233   
     /**
 234   
      * Sets the log to write to.
 235   
      *  
 236   
      * @param theLog The log to set
 237   
      */
 238  0
     public void setLog(Log theLog)
 239   
     {
 240  0
         this.log = theLog;
 241   
     }
 242   
 
 243   
     /**
 244   
      * Sets the time to wait after the container has been shut down.
 245   
      * 
 246   
      * The default time is 2 seconds.
 247   
      * 
 248   
      * @param theShutDownWait The time to wait in milliseconds
 249   
      */
 250  0
     public void setShutDownWait(long theShutDownWait)
 251   
     {
 252  0
         this.shutDownWait = theShutDownWait;
 253   
     }
 254   
 
 255   
     /**
 256   
      * Sets the timeout after which to stop trying to call the container.
 257   
      * 
 258   
      * The default timeout is 3 minutes.
 259   
      * 
 260   
      * @param theTimeout The timeout in milliseconds
 261   
      */
 262  0
     public void setTimeout(long theTimeout)
 263   
     {
 264  0
         this.timeout = theTimeout;
 265   
     }
 266   
 
 267   
     /**
 268   
      * Sets the HTTP/HTTPS URL that will be continuously pinged to check if the
 269   
      * container is running.
 270   
      * 
 271   
      * @param theTestURL The URL to set
 272   
      */
 273  0
     public void setURL(URL theTestURL)
 274   
     {
 275  0
         if (!(theTestURL.getProtocol().equalsIgnoreCase("http") 
 276   
             || theTestURL.getProtocol().equalsIgnoreCase("https")))
 277   
         {
 278  0
             throw new IllegalArgumentException("Not a HTTP or HTTPS URL");
 279   
         } 
 280  0
         this.testURL = theTestURL;
 281   
     }
 282   
 
 283   
     // Private Methods ---------------------------------------------------------
 284   
 
 285   
     /**
 286   
      * Tests whether we are able to connect to the HTTP server identified by the
 287   
      * specified URL.
 288   
      * 
 289   
      * @param theUrl The URL to check
 290   
      * @return the HTTP response code or -1 if no connection could be 
 291   
      *         established
 292   
      */
 293  0
     private int testConnectivity(URL theUrl)
 294   
     {
 295  0
         int code;
 296  0
         try
 297   
         {
 298  0
             HttpURLConnection connection = 
 299   
                 (HttpURLConnection) theUrl.openConnection();
 300  0
             connection.setRequestProperty("Connection", "close");
 301  0
             connection.connect();
 302  0
             readFully(connection);
 303  0
             connection.disconnect();
 304  0
             code = connection.getResponseCode();
 305   
         }
 306   
         catch (IOException e)
 307   
         {
 308  0
             this.log.debug("Failed to connect to [" + theUrl + "]", e);
 309  0
             code = -1;
 310   
         }
 311  0
         return code;
 312   
     }
 313   
 
 314   
 
 315   
     /**
 316   
      * Tests whether an HTTP return code corresponds to a valid connection
 317   
      * to the test URL or not. Success is 200 up to but excluding 300.
 318   
      * 
 319   
      * @param theCode the HTTP response code to verify
 320   
      * @return <code>true</code> if the test URL could be called without error,
 321   
      *         <code>false</code> otherwise
 322   
      */
 323  0
     private boolean isAvailable(int theCode)
 324   
     {
 325  0
         boolean result;
 326  0
         if ((theCode != -1) && (theCode < 300)) 
 327   
         {
 328  0
             result = true;            
 329   
         }
 330   
         else
 331   
         {
 332  0
             result = false;
 333   
         }
 334  0
         return result;
 335   
     }
 336   
 
 337   
     /**
 338   
      * Retrieves the server name of the container.
 339   
      * 
 340   
      * @param theUrl The URL to retrieve
 341   
      * @return The server name, or <code>null</code> if the server name could 
 342   
      *         not be retrieved
 343   
      */
 344  0
     private String retrieveServerName(URL theUrl)
 345   
     {
 346  0
         String retVal = null;
 347  0
         try
 348   
         {
 349  0
             HttpURLConnection connection = 
 350   
                 (HttpURLConnection) theUrl.openConnection();
 351  0
             connection.connect();
 352  0
             retVal = connection.getHeaderField("Server");
 353  0
             connection.disconnect();
 354   
         }
 355   
         catch (IOException e)
 356   
         {
 357  0
             this.log.debug("Could not get server name from [" 
 358   
                 + theUrl + "]", e);
 359   
         }
 360  0
         return retVal;
 361   
     }
 362   
 
 363   
     /**
 364   
      * Fully reads the input stream from the passed HTTP URL connection to
 365   
      * prevent (harmless) server-side exception.
 366   
      *
 367   
      * @param theConnection the HTTP URL connection to read from
 368   
      * @exception IOException if an error happens during the read
 369   
      */
 370  0
     static void readFully(HttpURLConnection theConnection)
 371   
                    throws IOException
 372   
     {
 373   
         // Only read if there is data to read ... The problem is that not
 374   
         // all servers return a content-length header. If there is no header
 375   
         // getContentLength() returns -1. It seems to work and it seems
 376   
         // that all servers that return no content-length header also do
 377   
         // not block on read() operations!
 378  0
         if (theConnection.getContentLength() != 0)
 379   
         {
 380  0
             byte[] buf = new byte[256];
 381  0
             InputStream in = theConnection.getInputStream();
 382  0
             while (in.read(buf) != -1)
 383   
             {
 384   
                 // Make sure we read all the data in the stream
 385   
             }
 386   
         }
 387   
     }
 388   
 
 389   
     /**
 390   
      * Pauses the current thread for the specified amount.
 391   
      *
 392   
      * @param theMs The time to sleep in milliseconds
 393   
      * @throws BuildException If the sleeping thread is interrupted
 394   
      */
 395  0
     private void sleep(long theMs) throws BuildException
 396   
     {
 397  0
         try
 398   
         {
 399  0
             Thread.sleep(theMs);
 400   
         }
 401   
         catch (InterruptedException e)
 402   
         {
 403  0
             throw new BuildException("Interruption during sleep", e);
 404   
         }
 405   
     }
 406   
 }
 407