From 5808bc275c42b3d12f23f9b04b7549417ea4c6dc Mon Sep 17 00:00:00 2001
From: kenneth_suter <kenneth_suter@localhost>
Date: Fri, 27 Apr 2007 21:30:01 +0000
Subject: [PATCH] implements code to access and parse the newly published upgrade build availability information published at http://www.opends.org/upgrade-builds

---
 opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Build.java                  |  135 +++++++++++++++++++---
 opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java               |   10 +
 opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/ui/UpgraderReviewPanel.java |   22 ++
 opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/RemoteBuildManager.java     |  175 +++++++++++++++++++----------
 4 files changed, 254 insertions(+), 88 deletions(-)

diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Build.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Build.java
index 9cd4b12..13493a2 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Build.java
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Build.java
@@ -28,23 +28,81 @@
 package org.opends.quicksetup.upgrader;
 
 import java.net.URL;
+import java.util.EnumSet;
 
 /**
    * Representation of an OpenDS build package.
  */
 public class Build implements Comparable<Build> {
 
-  private URL url;
-  private String id;
+  /**
+   * Describes build types.
+   */
+  enum Category {
+
+    /**
+     * Daily build descriptor.
+     */
+    DAILY("Daily Build"),
+
+    /**
+     * Weekly build descriptor.
+     */
+    WEEKLY("Weekly Build"),
+
+    /**
+     * Release build descriptor.
+     */
+    RELEASE("Release Build");
+
+    /**
+     * Creates a Category from its 'key' String value.
+     * @param s String representing a key
+     * @return the Category corresponding to the input <code>key</code>; null
+     * if the input string is not a category key
+     */
+    public static Category fromString(String s) {
+      Category category = null;
+      for (Category c : EnumSet.allOf(Category.class)) {
+        if (c.key.equals(s)) {
+          category = c;
+          break;
+        }
+      }
+      return category;
+    }
+
+    String key;
+
+    private Category(String key) {
+      this.key = key;
+    }
+
+    /**
+     * Gets the string that represents this category in
+     * the build information page.
+     * @return String key
+     */
+    public String getKey() {
+      return key;
+    }
+
+  }
+
+  private URL downloadUrl;
+  private String displayName;
+  private Category category;
 
   /**
    * Creates an instance.
-   * @param url where the build package can be accessed
-   * @param id of the new build
+   * @param displayName where the build package can be accessed
+   * @param downloadUrl of the new build
+   * @param category build category
    */
-  Build(URL url, String id) {
-    this.url = url;
-    this.id = id;
+  Build(String displayName, URL downloadUrl, Category category) {
+    this.displayName = displayName;
+    this.downloadUrl = downloadUrl;
+    this.category = category;
   }
 
   /**
@@ -52,16 +110,7 @@
    * @return URL representing access to the build package
    */
   public URL getUrl() {
-    return url;
-  }
-
-  /**
-   * Gets the builds ID number, a 14 digit number representing the time
-   * the build was created.
-   * @return String represenging the build
-   */
-  public String getId() {
-    return id;
+    return this.downloadUrl;
   }
 
   /**
@@ -69,7 +118,15 @@
    * @return String representing this build
    */
   public String getDisplayName() {
-    return getId();
+    return this.displayName;
+  }
+
+  /**
+   * Gets the category of this build.
+   * @return Category indicating the type of this build.
+   */
+  public Category getCategory() {
+    return this.category;
   }
 
   /**
@@ -83,7 +140,47 @@
    * {@inheritDoc}
    */
   public int compareTo(Build o) {
-    return getDisplayName().compareTo(o.getDisplayName());
+    if (o == null) throw new NullPointerException();
+    int c = getCategory().compareTo(o.getCategory());
+    if (c == 0) {
+      c = getDisplayName().compareTo(o.getDisplayName());
+    }
+    return c;
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  public int hashCode() {
+    int hc = 11;
+    Category cat = getCategory();
+    if (cat != null) {
+      hc = 31 * hc + cat.hashCode();
+    }
+    String disp = getDisplayName();
+    if (disp != null) {
+      hc = 31 * hc + disp.hashCode();
+    }
+    return hc;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    boolean eq = false;
+    if (obj != null && obj instanceof Build) {
+      Category thisCat = getCategory();
+      Category thatCat = ((Build)obj).getCategory();
+      if ((thisCat != null && thisCat.equals(thatCat)) ||
+          (thisCat == null && thatCat == null)) {
+        String thisDisp = getDisplayName();
+        String thatDisp = ((Build)obj).getDisplayName();
+        eq = ((thisDisp != null && thisDisp.equals(thatDisp)) ||
+                (thisDisp == null && thatDisp == null));
+      }
+    }
+    return eq;
+  }
 }
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/RemoteBuildManager.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/RemoteBuildManager.java
index b9967c9..5d4eff3 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/RemoteBuildManager.java
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/RemoteBuildManager.java
@@ -34,11 +34,11 @@
 import java.net.URL;
 import java.net.Proxy;
 import java.net.URLConnection;
+import java.net.MalformedURLException;
 import java.util.*;
 import java.util.List;
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
 import java.util.logging.Logger;
+import java.util.logging.Level;
 import java.io.*;
 import java.awt.*;
 
@@ -50,26 +50,13 @@
   static private final Logger LOG =
           Logger.getLogger(RemoteBuildManager.class.getName());
 
-  /**
-   * Describes build types.
-   */
-  enum BuildType {
-
-    /**
-     * Nightly build descriptor.
-     */
-    NIGHTLY,
-
-    /**
-     * Weekly build descriptor.
-     */
-    WEEKLY
-
-  }
-
   private Application app;
 
-  private URL url;
+  /**
+   * This URL is expected to point at a list of the builds parsable by
+   * the <code>RemoteBuildsPageParser</code>.
+   */
+  private URL buildListUrl;
 
   private Proxy proxy;
 
@@ -80,11 +67,11 @@
   /**
    * Creates an instance.
    * @param app using this tool
-   * @param url base context for an OpenDS build repository
+   * @param url base context for an OpenDS build list
    */
   public RemoteBuildManager(Application app, URL url) {
     this.app = app;
-    this.url = url;
+    this.buildListUrl = url;
   }
 
   /**
@@ -92,7 +79,7 @@
    * @return URL representing base context of the build repo
    */
   public URL getBaseContext() {
-    return this.url;
+    return this.buildListUrl;
   }
 
   /**
@@ -105,38 +92,9 @@
    * from the build repository
    */
   public List<Build> listBuilds(InputStream in) throws IOException {
-    List<Build> buildList = new ArrayList<Build>();
     String dailyBuildsPage = downloadDailyBuildsPage(in);
-    Pattern p = Pattern.compile("\\d{14}");
-    Matcher m = p.matcher(dailyBuildsPage);
-    Set<String> buildIds = new HashSet<String>();
-    while (m.find()) {
-      buildIds.add(dailyBuildsPage.substring(m.start(), m.end()));
-    }
-
-//    for (String buildId : buildIds) {
-//      // TODO:  this needs to be changed
-//      URL buildUrl =
-//              new URL(url, "daily-builds/" +
-//                      buildId +
-//                      "/OpenDS/build/package/OpenDS-0.1.zip");
-//      buildList.add(new Build(url, buildId));
-//    }
-
-    // This is encoded in build.xml.  We might need a more dynamic
-    // way of getting this information.
-    StringBuilder latestContextSb = new StringBuilder()
-            .append("daily-builds/latest/OpenDS/build/package/OpenDS-")
-            .append(org.opends.server.util.DynamicConstants.MAJOR_VERSION)
-            .append(".")
-            .append(org.opends.server.util.DynamicConstants.MINOR_VERSION)
-            .append(org.opends.server.util.DynamicConstants.VERSION_QUALIFIER)
-            .append(".zip");
-    Build latest = new Build(new URL(url, latestContextSb.toString()),
-                            "Latest");
-    buildList.add(latest);
-    Collections.sort(buildList);
-    return buildList;
+    return Collections.unmodifiableList(
+      RemoteBuildsPageParser.parseBuildList(dailyBuildsPage));
   }
 
   /**
@@ -150,12 +108,11 @@
                                                final Object o)
     throws IOException
   {
-    URL dailyBuildsUrl = new URL(url, "daily-builds");
     URLConnection conn;
     if (proxy == null) {
-      conn = dailyBuildsUrl.openConnection();
+      conn = buildListUrl.openConnection();
     } else {
-      conn = dailyBuildsUrl.openConnection(proxy);
+      conn = buildListUrl.openConnection(proxy);
     }
     String proxyAuthString = createProxyAuthString();
     if (proxyAuthString != null) {
@@ -184,7 +141,7 @@
     StringBuilder builder = new StringBuilder();
     String line;
     while (null != (line = reader.readLine())) {
-      builder.append(line);
+      builder.append(line).append('\n');
     }
     return builder.toString();
   }
@@ -322,6 +279,104 @@
   }
 
   /**
+   * Parser for the web page that lists available builds.  This pag is expected
+   * to be a tab-delimited text document where each line represents a build with
+   * the following fields:
+   * 1.  A build display name (e.g. OpenDS 0.1 Build 036)
+   * 2.  A URL where the build's .zip file can be downloaded
+   * 3.  A category string for the build (e.g. Weekly Build, Daily Build)
+   */
+  static private class RemoteBuildsPageParser {
+
+    /**
+     * Parses a string representing the build information list into a list
+     * of builds sorted by usefulness meaning that release builds are first,
+     * followed by weekly builds and finally daily builds.
+     * @param page String representing the build info page
+     * @return List of Builds
+     */
+    static public List<Build> parseBuildList(String page) {
+      List<Build> builds = new ArrayList<Build>();
+      if (page != null) {
+        BufferedReader reader = new BufferedReader(new StringReader(page));
+        String line;
+        try {
+          while (null != (line = reader.readLine())) {
+            try {
+              Build build = parseBuildLine(line);
+              builds.add(build);
+            } catch (IllegalArgumentException iae) {
+              StringBuffer msg = new StringBuffer()
+                      .append("Error parsing line '")
+                      .append(line)
+                      .append("': ")
+                      .append(iae.getMessage());
+              LOG.log(Level.INFO, msg.toString());
+            }
+          }
+        } catch (IOException e) {
+          LOG.log(Level.INFO, "error", e);
+        }
+      } else {
+        LOG.log(Level.WARNING, "build list page is null");
+      }
+      return builds;
+    }
+
+    static private Build parseBuildLine(String line)
+            throws IllegalArgumentException {
+      String displayName = null;
+      String downloadUrlString = null;
+      String categoryString = null;
+      URL downloadUrl;
+      Build.Category category;
+      StringTokenizer st = new StringTokenizer(line, "\t");
+      if (st.hasMoreTokens()) {
+        displayName = st.nextToken();
+      }
+      if (st.hasMoreTokens()) {
+        downloadUrlString = st.nextToken();
+      }
+      if (st.hasMoreTokens()) {
+        categoryString = st.nextToken();
+      }
+      if (displayName == null ||
+              downloadUrlString == null ||
+              categoryString == null) {
+        StringBuffer msg = new StringBuffer()
+                .append("Line '")
+                .append(line)
+                .append("' is incomplete or is not correctly delimited")
+                .append("with tab characters");
+        throw new IllegalArgumentException(msg.toString());
+      } else {
+
+        try {
+          downloadUrl = new URL(downloadUrlString);
+        } catch (MalformedURLException e) {
+          StringBuffer msg = new StringBuffer()
+                  .append("URL '")
+                  .append(downloadUrlString)
+                  .append("' is invalid");
+          throw new IllegalArgumentException(msg.toString());
+        }
+        category = Build.Category.fromString(categoryString);
+        if (category == null) {
+          StringBuffer msg = new StringBuffer()
+                  .append("Category '")
+                  .append(categoryString)
+                  .append("' is invalid; must be one of ");
+          for (Build.Category c : EnumSet.allOf(Build.Category.class)) {
+            msg.append("'").append(c.getKey()).append("' ");
+          }
+          throw new IllegalArgumentException(msg.toString());
+        }
+      }
+      return new Build(displayName, downloadUrl, category);
+    }
+  }
+
+  /**
    * For testing only.
    * @param args command line arguments
    */
@@ -335,8 +390,8 @@
 //
 //      System.setProperties(systemSettings);
 //
-//      URL url = new URL("http://builds.opends.org");
-//      RemoteBuildManager rbm = new RemoteBuildManager(null, url);
+//      URL buildListUrl = new URL("http://builds.opends.org");
+//      RemoteBuildManager rbm = new RemoteBuildManager(null, buildListUrl);
 //      //List<Build> builds = rbm.listBuilds();
 //      //for (Build build : builds) {
 //      //  System.out.println("build " + build);
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java
index 305e7f1..4c7956f 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java
@@ -240,10 +240,12 @@
   public RemoteBuildManager getRemoteBuildManager() {
     if (remoteBuildManager == null) {
       try {
-        // TODO: make this configurable.
-        // The slash at the end of the URL was/is important in getting the
-        // correct redirection from the web server
-        URL buildRepo = new URL("http://builds.opends.org/");
+        String listUrlString =
+                System.getProperty("org.opends.quicksetup.upgrader.BuildList");
+        if (listUrlString == null) {
+          listUrlString = "http://www.opends.org/upgrade-builds";
+        }
+        URL buildRepo = new URL(listUrlString);
         remoteBuildManager = new RemoteBuildManager(this, buildRepo);
       } catch (MalformedURLException e) {
         LOG.log(Level.INFO, "error", e);
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/ui/UpgraderReviewPanel.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/ui/UpgraderReviewPanel.java
index 63324ad..bdb8aa9 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/ui/UpgraderReviewPanel.java
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/ui/UpgraderReviewPanel.java
@@ -71,8 +71,15 @@
    */
   public void beginDisplay(UserData data) {
     tcServerLocation.setText(getServerToUpgrade());
-    tcOldBuild.setText(getOldBuildId());
-    tcNewBuild.setText(getNewBuildId());
+
+    // Unfortunately these string are different.  The
+    // old build string is the build ID (14 digit number)
+    // and the new build is the build display name that
+    // appears in the available builds information page.
+    // It is currently not feasible to correlate these.
+    tcOldBuild.setText(getOldBuildString());
+    tcNewBuild.setText(getNewBuildString());
+
   }
 
   /**
@@ -175,7 +182,7 @@
     return getUserData().getServerLocation();
   }
 
-  private String getOldBuildId() {
+  private String getOldBuildString() {
     String oldVersion;
     try {
       oldVersion = getApplication().getInstallation().getBuildId();
@@ -186,12 +193,17 @@
     return oldVersion;
   }
 
-  private String getNewBuildId() {
+  /**
+   * Gets the string by which the new build is known in the
+   * available builds page.
+   * @return String indicating the new build
+   */
+  private String getNewBuildString() {
     String newVersion;
     UpgradeUserData uud = (UpgradeUserData)getUserData();
     Build build = uud.getInstallPackageToDownload();
     if (build != null) {
-      newVersion = build.getId();
+      newVersion = build.getDisplayName();
     } else {
       // TODO: figure out the build from the zip somehow
       newVersion = getMsg("upgrade-build-id-unknown");

--
Gitblit v1.10.0