开发者

How can I get Ant to load and check a URL resource in a 'waitfor' task?

I am working on an Ant target for running tests, and I need to have an app server up and running before the tests start. I'm using the waitFor task to ping a URL until it gets a response. This works, except that I need to not only make sure the URL is simply accessible, but also check that a particular status string is present. This second requirement is simple on its own, with a get or telnet task, followed by a substring search. But the 'waitfor' task doesn't accept any nested elements other than standard Ant conditions, or things they can wrap (things that return booleans). I have tried loading the URL as a resource prior to running the 'waitfor' task with a resourceconatins condition, but that caches the response, and doesn't refresh from the server.

Is there a way to force the URL to be loaded on each loop? Or some other way to achieve this?

Here's what I want to achieve in pseudo-code:

<waitfor>
    <and>
        <!-- make sure the URL is accessible at all -->
        <http url="theurl.jsp"/>

        <!-- load the URL as a resource and make sure it contains a substring -->
        <!-- moving the "url" task before the waitfor works i开发者_如何学Pythonf all conditions are met the first time through, but does not refresh the URL if it has to loop over again. having it inside like this, fails because waitfor/and don't support nexted "url" tasks -->
        <url id="url.resource" url="url.jsp"/>
        <resourcecontains refid="url.resource" substring="status"/>
    </and>
    <!-- else, keep on trying until timeout -->
</waitfor>
<!-- continue on to the tests to be run -->


Usage:

   <typedef
      name="httpcontains"
      classname="HttpContains"
   />

  <waitfor maxwait="10" maxwaitunit="minute" checkevery="500">
    <httpcontains contains="Running" url="http://myurl"/>
  </waitfor>

Code below allows for this:

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectComponent;
import org.apache.tools.ant.taskdefs.condition.Condition;

/**
 * Condition to wait for a HTTP request to succeed. Its attribute(s) are:
 *   url - the URL of the request.
 *   errorsBeginAt - number at which errors begin at; default=400.
 * @since Ant 1.5
 */
public class HttpContains extends ProjectComponent implements Condition {
    private String spec = null;
    private String contains = null;
    /**
     * Set the url attribute
     *
     * @param url the url of the request
     */
    public void setUrl(String url) {
        spec = url;
    }

    public void setContains(String s){
       contains = s;
    }

    private int errorsBeginAt = 400;

    /**
     * Set the errorsBeginAt attribute
     *
     * @param errorsBeginAt number at which errors begin at, default is
     *                      400
     */
    public void setErrorsBeginAt(int errorsBeginAt) {
        this.errorsBeginAt = errorsBeginAt;
    }

    /**
     * @return true if the HTTP request succeeds
     * @exception BuildException if an error occurs
     */
    public boolean eval() throws BuildException {
        if (spec == null) {
            throw new BuildException("No url specified in http condition");
        }
        log("Checking for " + spec, Project.MSG_VERBOSE);
        try {
            URL url = new URL(spec);
            try {
                URLConnection conn = url.openConnection();
                if (conn instanceof HttpURLConnection) {
                    HttpURLConnection http = (HttpURLConnection) conn;
                    int code = http.getResponseCode();
                    log("Result code for " + spec + " was " + code,
                        Project.MSG_VERBOSE);
                    if (code > 0 && code < errorsBeginAt) 
                    {
                       if ( evalContents(url) )
                          return true;
                       else
                          return false;

                    } else {
                        return false;
                    }
                }
            } catch (java.io.IOException e) {
                return false;
            }
        } catch (MalformedURLException e) {
            throw new BuildException("Badly formed URL: " + spec, e);
        }
        return true;
    }

    public boolean evalContents(URL mUrl )
    {
       try
       {

          String contents;

          StringBuffer buffer = new StringBuffer();
          byte[] buf = new byte[1024];
          int amount = 0;
          InputStream input = mUrl.openStream();

          while ((amount = input.read(buf)) > 0) 
          {   
             buffer.append(new String(buf, 0, amount));
          }
          input.close();
          contents = buffer.toString();

          log("Result code for " + contents,
                Project.MSG_VERBOSE);

          if ( contents.indexOf(contains) > -1 )
          {

             log("Result code for " + contents.indexOf(contains),
                   Project.MSG_VERBOSE);
             return true;

          }






          return false;
       }
       catch ( Exception e )
       {
          e.printStackTrace();
          log("Result code for " + e.toString(),
                Project.MSG_VERBOSE);

          return false;
       }
    }
}


You can achieve this using a target called from a scriptcondition element in the waitfor. Here's an example.

To use it, you need to set theurl and the content.matches properties to be the resource in question, and the string to match respectively. Adjust the 'maxwait' settings as required.

Property theurl.fail is set on timeout.

<target name="lr">
    <local name="local.data" />
    <loadresource property="local.data">
        <url url="${theurl}" />
    </loadresource>
    <condition property="theurl.matches">
       <matches pattern="${content.matches}" string="${local.data}"/>
    </condition>
</target>

<waitfor maxwait="10" maxwaitunit="second" timeoutproperty="theurl.fail">
    <scriptcondition language="javascript" value="false">
    <![CDATA[
        project.getTargets().get( "lr" ).execute( );
        if ( project.getProperty( "theurl.matches" ) )
            self.setValue( true );
    ]]>
    </scriptcondition>
</waitfor>

This takes advantage of the Ant local task that was new in v1.8. The lr target fetches the URL and looks for the match, if found the property theurl.matches is set. The scriptcondition executes the target, embedded within the waitfor, until success or timeout.

I tested this against the US Naval Observatory Master Clock Time and various short time strings, seemed to work nicely.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜