From 0da3881226d344bbb81ca9bfe2a3c1bff0014027 Mon Sep 17 00:00:00 2001
From: jacobrobertson <jacob.robertson.work@gmail.com>
Date: Sun, 13 Nov 2011 08:23:50 -0600
Subject: [PATCH] adding regex support

---
 pom.xml                                       |  7 ++
 .../sitemonitor/SiteMonitorDescriptor.java    | 41 ++++++++--
 .../sitemonitor/SiteMonitorRecorder.java      | 48 +++++++++++-
 .../sitemonitor/SiteMonitorValidator.java     | 19 +++++
 .../plugins/sitemonitor/model/Site.java       | 76 +++++++++++++++++++
 .../plugins/sitemonitor/Messages.properties   |  3 +
 .../SiteMonitorRecorder/config.jelly          | 16 +++-
 src/main/webapp/url.html                      |  7 +-
 .../plugins/sitemonitor/model/SiteTest.java   | 12 +++
 9 files changed, 218 insertions(+), 11 deletions(-)
 mode change 100755 => 100644 src/main/resources/hudson/plugins/sitemonitor/Messages.properties

diff --git a/pom.xml b/pom.xml
index 4c871f5..59a44e7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -69,6 +69,13 @@
                     </systemPropertyVariables>
                 </configuration>
             </plugin>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
     
diff --git a/src/main/java/hudson/plugins/sitemonitor/SiteMonitorDescriptor.java b/src/main/java/hudson/plugins/sitemonitor/SiteMonitorDescriptor.java
index 8d5b9ce..f938c93 100644
--- a/src/main/java/hudson/plugins/sitemonitor/SiteMonitorDescriptor.java
+++ b/src/main/java/hudson/plugins/sitemonitor/SiteMonitorDescriptor.java
@@ -152,15 +152,13 @@ public final Publisher newInstance(final StaplerRequest request,
 
         Object sitesObject = json.get("sites");
         if (sitesObject instanceof JSONObject) {
-            for (Object siteObject : json.getJSONObject("sites").values()) {
-                String url = String.valueOf(siteObject);
-                sites.add(new Site(url));
-            }
+            Site site = toSite((JSONObject) sitesObject);
+            sites.add(site);
         } else if (sitesObject instanceof JSONArray) {
             for (Object siteObject : (JSONArray) sitesObject) {
                 if (siteObject instanceof JSONObject) {
-                    String url = ((JSONObject) siteObject).getString("url");
-                    sites.add(new Site(url));
+                    Site site = toSite((JSONObject) siteObject);
+                    sites.add(site);
                 }
             }
         } else {
@@ -170,6 +168,19 @@ public final Publisher newInstance(final StaplerRequest request,
         return new SiteMonitorRecorder(sites);
     }
 
+    /**
+     * Converts the json object to the Site.
+     * @param siteObject 
+     *            the siteObject submitted
+     * @return the new Site
+     */
+    private Site toSite(JSONObject siteObject) {
+        String url = siteObject.getString("url");
+        String regex = siteObject.getString("regularExpression");
+        boolean regexFlag = siteObject.getBoolean("failWhenRegexNotFound");
+        return new Site(url, regex, regexFlag);
+    }
+    
     /**
      * Handles SiteMonitor global configuration per Jenkins instance.
      * @param request
@@ -205,6 +216,15 @@ public final FormValidation doCheckUrl(@QueryParameter final String value) {
         return mValidator.validateUrl(value);
     }
 
+    /**
+     * @param value
+     *            the value to validate
+     * @return true if value is a valid Regex, false otherwise
+     */
+    public final FormValidation doCheckRegex(@QueryParameter final String value) {
+        return mValidator.validateRegex(value);
+    }
+
     /**
      * @param value
      *            the value to validate
@@ -225,4 +245,13 @@ public final FormValidation doCheckTimeout(
             @QueryParameter final String value) {
         return mValidator.validateTimeout(value);
     }
+
+    /**
+     * @return the url to the help fule
+     */
+    @Override
+    public String getHelpFile() {
+        return "/plugin/sitemonitor/url.html";
+    }
+
 }
diff --git a/src/main/java/hudson/plugins/sitemonitor/SiteMonitorRecorder.java b/src/main/java/hudson/plugins/sitemonitor/SiteMonitorRecorder.java
index 946a669..3497674 100644
--- a/src/main/java/hudson/plugins/sitemonitor/SiteMonitorRecorder.java
+++ b/src/main/java/hudson/plugins/sitemonitor/SiteMonitorRecorder.java
@@ -35,7 +35,6 @@
 import java.net.MalformedURLException;
 import java.net.SocketTimeoutException;
 import java.net.URL;
-import java.net.UnknownHostException;
 import java.security.KeyManagementException;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
@@ -48,8 +47,12 @@
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;
+
+import org.apache.commons.io.IOUtils;
+
 import java.util.ArrayList;
 import java.util.List;
+import java.util.regex.Matcher;
 
 /**
  * Performs the web site monitoring process.
@@ -161,6 +164,13 @@ public final boolean perform(final AbstractBuild<?, ?> build,
                 } else {
                     status = Status.ERROR;
                 }
+                
+                if (status == Status.UP && site.getRegularExpressionPattern() != null) {
+                    Result regexResult = validateWithRegularExpression(site, connection);
+                    note = regexResult.getNote();
+                    status = regexResult.getStatus();
+                }
+                
             } catch (SocketTimeoutException ste) {
                 listener.getLogger().println(ste + " - " + ste.getMessage());
                 status = Status.DOWN;
@@ -203,6 +213,42 @@ public final boolean perform(final AbstractBuild<?, ?> build,
         return !hasFailure;
     }
     
+    /**
+     * @param site 
+     *            the Site configuration object
+     * @param connection 
+     *            the connection to the site
+     * @return the Result with the status and note
+     * @throws IOException 
+     *            When any IO fails
+     */
+    private Result validateWithRegularExpression(Site site, HttpURLConnection connection) throws IOException {
+        String page = IOUtils.toString(connection.getInputStream());
+        Matcher matcher = site.getRegularExpressionPattern().matcher(page);
+        boolean found = matcher.find();
+        String foundString = null;
+        boolean exact = false;
+        if (found) {
+            foundString = matcher.group();
+            if (site.getRegularExpression().equals(foundString)) {
+                exact = true;
+            }
+        }
+        Status status = Status.UP;
+        if (site.isFailWhenRegexNotFound() != found) {
+               status = Status.DOWN;
+        }
+        String note;
+        if (exact) {
+            note = Messages.SiteMonitor_Status_RegularExpressionExactMatch(foundString);
+        } else if (found) {
+            note = Messages.SiteMonitor_Status_RegularExpressionFound(site.getRegularExpression(), foundString);
+        } else {
+            note = Messages.SiteMonitor_Status_RegularExpressionNotFound(site.getRegularExpression());
+        }
+        return new Result(null, 0, status, note);
+    }
+    
     /**
      * Gets the required monitor service.
      * @return the BuildStepMonitor
diff --git a/src/main/java/hudson/plugins/sitemonitor/SiteMonitorValidator.java b/src/main/java/hudson/plugins/sitemonitor/SiteMonitorValidator.java
index b4dbc04..1108aa8 100644
--- a/src/main/java/hudson/plugins/sitemonitor/SiteMonitorValidator.java
+++ b/src/main/java/hudson/plugins/sitemonitor/SiteMonitorValidator.java
@@ -27,6 +27,7 @@
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.regex.Pattern;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.math.NumberUtils;
@@ -60,6 +61,24 @@ public final FormValidation validateUrl(final String url) {
         return validation;
     }
 
+    /**
+     * Validates a regex.
+     * @param regex
+     *            the regex
+     * @return false when regex is malformed, true otherwise
+     */
+    public final FormValidation validateRegex(final String regex) {
+        FormValidation validation = FormValidation.ok();
+        if (StringUtils.isNotBlank(regex)) {
+            try {
+                Pattern.compile(regex);
+            } catch (Exception e) {
+                validation = FormValidation.error(e, e.getLocalizedMessage());
+            }
+        }
+        return validation;
+    }
+
     /**
      * Validates HTTP connection timeout value.
      * @param timeout
diff --git a/src/main/java/hudson/plugins/sitemonitor/model/Site.java b/src/main/java/hudson/plugins/sitemonitor/model/Site.java
index 4fbd16e..f158b2f 100644
--- a/src/main/java/hudson/plugins/sitemonitor/model/Site.java
+++ b/src/main/java/hudson/plugins/sitemonitor/model/Site.java
@@ -21,6 +21,10 @@
  */
 package hudson.plugins.sitemonitor.model;
 
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.StringUtils;
+
 /**
  * This class keeps the details of the web site to be monitored.
  * @author cliffano
@@ -32,6 +36,37 @@ public class Site {
      */
     private String mUrl;
 
+    /**
+     * The regular expression to check.
+     */
+    private String mRegularExpression;
+    
+    /**
+     * The pattern for the expression to check.
+     */
+    transient private Pattern mRegularExpressionPattern;
+
+    /**
+     * The flag for checking the regex.
+     */
+    private boolean mFailWhenRegexNotFound = true;
+
+    /**
+     * Constructs a Site with specified details.
+     * @param url
+     *            the web site URL
+     * @param regularExpression
+     *            the regular expression to check
+     * @param failWhenRegexNotFound
+     *            the flag for checking the regex
+     */
+    public Site(final String url, final String regularExpression, final boolean failWhenRegexNotFound) {
+        mUrl = url;
+        mRegularExpression = regularExpression;
+        mFailWhenRegexNotFound = failWhenRegexNotFound;
+        setRegexPattern();
+    }
+
     /**
      * Constructs a Site with specified details.
      * @param url
@@ -41,10 +76,51 @@ public Site(final String url) {
         mUrl = url;
     }
 
+    /**
+     * Sets the regex pattern based on the regex.
+     */
+    private void setRegexPattern() {
+        if (!StringUtils.isEmpty(mRegularExpression)) {
+            mRegularExpressionPattern = Pattern.compile(mRegularExpression);
+        } else {
+            mRegularExpressionPattern = null;
+        }
+    }
+
+    /**
+     * Part of the serialization strategy.
+     * @return this
+     */
+    Object readResolve() {
+        setRegexPattern();
+        return this;
+    }
+    
     /**
      * @return the web site URL
      */
     public final String getUrl() {
         return mUrl;
     }
+
+    /**
+     * @return the regular expression to check
+     */
+    public final String getRegularExpression() {
+        return mRegularExpression;
+    }
+
+    /**
+     * @return the Pattern for the regular expression to check
+     */
+    public final Pattern getRegularExpressionPattern() {
+        return mRegularExpressionPattern;
+    }
+
+    /**
+     * @return the flag for checking the regex
+     */
+    public final boolean isFailWhenRegexNotFound() {
+        return mFailWhenRegexNotFound;
+    }
 }
diff --git a/src/main/resources/hudson/plugins/sitemonitor/Messages.properties b/src/main/resources/hudson/plugins/sitemonitor/Messages.properties
old mode 100755
new mode 100644
index 7bde220..50dc075
--- a/src/main/resources/hudson/plugins/sitemonitor/Messages.properties
+++ b/src/main/resources/hudson/plugins/sitemonitor/Messages.properties
@@ -5,6 +5,9 @@ SiteMonitor.Error.PrefixOfURL=URL must start with http:// or https://
 SiteMonitor.Error.TimeoutIsBlank=Timeout value must be provided
 SiteMonitor.Error.TimeoutIsNotDigit=Timeout value must be a number
 SiteMonitor.Error.InvalidResponseCode=Invalid response code(s): 
+SiteMonitor.Status.RegularExpressionNotFound=Regular expression "{0}" was not found.
+SiteMonitor.Status.RegularExpressionExactMatch=Exact text "{0}" was found.
+SiteMonitor.Status.RegularExpressionFound=Regular expression "{0}" was found.<br/>Matching text was "{1}".
 SiteMonitor.Console.URL=URL: 
 SiteMonitor.Console.ResponseCode=response code: 
 SiteMonitor.Console.Status=status: 
diff --git a/src/main/resources/hudson/plugins/sitemonitor/SiteMonitorRecorder/config.jelly b/src/main/resources/hudson/plugins/sitemonitor/SiteMonitorRecorder/config.jelly
index 7312c89..16710e1 100644
--- a/src/main/resources/hudson/plugins/sitemonitor/SiteMonitorRecorder/config.jelly
+++ b/src/main/resources/hudson/plugins/sitemonitor/SiteMonitorRecorder/config.jelly
@@ -2,10 +2,20 @@
     <f:entry title="">
         <f:repeatable var="site" name="sites" items="${instance.sites}" noAddButton="true" minimum="1">
             <table style="width: 100%;">
-                <f:entry title="${%URL}" help="/plugin/sitemonitor/url.html">
+                <f:entry title="${%URL}">
                     <f:textbox name="url" value="${site.url}" checkUrl="'${rootURL}/publisher/SiteMonitorRecorder/checkUrl?value='+encode(this.value)"/>
-                    <div style="text-align: right;">
-                        <input type="button" value="${%Delete}" class="repeatable-delete show-if-not-only"/>
+                </f:entry>
+                <f:advanced>
+                    <f:entry title="${%Validate with regular expression}">
+                        <f:textbox name="regularExpression" value="${site.regularExpression}" checkUrl="'${rootURL}/publisher/SiteMonitorRecorder/checkRegex?value='+encode(this.value)"/>
+                    </f:entry>
+                    <f:entry title="${%Fail when Regular Expression is NOT found}">
+                        <f:checkbox name="failWhenRegexNotFound" field="failWhenRegexNotFound" default="true" />
+                    </f:entry>
+                </f:advanced>
+                <f:entry title="">
+                    <div align="right">
+                        <f:repeatableDeleteButton />
                     </div>
                 </f:entry>
                 <f:entry>
diff --git a/src/main/webapp/url.html b/src/main/webapp/url.html
index 52a2717..97bbc7c 100644
--- a/src/main/webapp/url.html
+++ b/src/main/webapp/url.html
@@ -1 +1,6 @@
-The URL address of the web site to be monitored. URL should start with http:// or https:// .
\ No newline at end of file
+Provides monitoring of one or more sites.  Each site can be configured with
+<ul>
+<li>The URL address of the web site to be monitored. URL should start with http:// or https:// .</li>
+<li>An optional Regular Expression to check for on the website's page.</li>
+<li>A flag indicating whether to fail/succeed when the regular expression is found.</li>
+</ul>
\ No newline at end of file
diff --git a/src/test/java/hudson/plugins/sitemonitor/model/SiteTest.java b/src/test/java/hudson/plugins/sitemonitor/model/SiteTest.java
index ee9788d..d198e15 100644
--- a/src/test/java/hudson/plugins/sitemonitor/model/SiteTest.java
+++ b/src/test/java/hudson/plugins/sitemonitor/model/SiteTest.java
@@ -11,4 +11,16 @@ public void testGetUrlShouldGiveExpectedUrlValue() {
         site = new Site("http://hudson-ci.org");
         assertEquals("http://hudson-ci.org", site.getUrl());
     }
+
+    public void testGetRegularExpressionShouldGiveExpectedUrlValue() {
+        site = new Site(null, "regex1", false);
+        assertEquals("regex1", site.getRegularExpression());
+        assertEquals("regex1", site.getRegularExpressionPattern().pattern());
+    }
+    
+    public void testGetRegularExpressionFlagShouldGiveExpectedUrlValue() {
+        site = new Site(null, null, false);
+        assertEquals(false, site.isFailWhenRegexNotFound());
+    }
+
 }