Index: /trunk/greenstone3-extensions/gsdl-as/build.xml
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/build.xml	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/build.xml	(revision 8738)
@@ -217,7 +217,7 @@
     <!-- Include all JAR files that will be included in /WEB-INF/lib -->
     <!-- *** CUSTOMIZE HERE AS REQUIRED BY YOUR APPLICATION *** -->
-<!--
-    <pathelement location="${foo.jar}"/>
--->
+    <fileset dir="lib">
+    	<include name="*.jar"/>
+    </fileset>
 
     <!-- Include all elements that Tomcat exposes to applications -->
Index: /trunk/greenstone3-extensions/gsdl-as/lib/templates/login.vm
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/lib/templates/login.vm	(revision 8738)
+++ /trunk/greenstone3-extensions/gsdl-as/lib/templates/login.vm	(revision 8738)
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Greenstone Digital Library Software</title>
+<link href="/alerting/form.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="content">
+<h3>Alerting Service for Greenstone</h3>
+<p>Login</p>
+#if ($error) 
+<div class="error">
+An error occured:<br/>
+$errormessage
+</div>
+#end
+<form action="/alerting/service?action=login" method="post"/>
+<table>
+<tr>
+<th><label for="username">username</label></th><td><input type="text" name="username" id="username"/></td>
+</tr>
+<tr>
+<th><label for="password">password</label></th><td><input type="password" name="password" id="password"/></td>
+</tr>
+</table>
+<br/>
+<input type="submit" value="login"/>
+</form>
+<div class="description">Don't have an account yet? <a href="/alerting/service?action=showRegistrationForm">Register</a> now!</a></div>
+</div>
+
+</body>
+</html>
Index: /trunk/greenstone3-extensions/gsdl-as/lib/templates/register.vm
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/lib/templates/register.vm	(revision 8738)
+++ /trunk/greenstone3-extensions/gsdl-as/lib/templates/register.vm	(revision 8738)
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Greenstone Digital Library Software</title>
+<link href="/alerting/form.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="content">
+<h3>Alerting Service for Greenstone</h3>
+<p>Register</p>
+#if ($error)
+<div class="error">
+An error occured:<br/>
+$errormessage
+</div>
+#end
+<form action="/alerting/service?action=register" method="post"/>
+<table>
+<tr>
+<th><label for="username">username</label></th><td><input type="text" name="username" id="username"/></td>
+</tr>
+<tr>
+<th><label for="password">password</label></th><td><input type="password" name="password" id="password"/></td>
+</tr>
+<tr>
+<th><label for="password2">repeat password</label></th><td><input type="password" name="password2" id="password2"/></td>
+</tr>
+</table>
+<br/>
+<input type="submit" value="create account"/>
+</form>
+</div>
+
+</body>
+</html>
Index: /trunk/greenstone3-extensions/gsdl-as/lib/templates/sub_collection.vm
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/lib/templates/sub_collection.vm	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/lib/templates/sub_collection.vm	(revision 8738)
@@ -37,18 +37,13 @@
 					<ol>
 					<li>You can choose one or more collections from the list below. Only events from the collections(s) you have chosen will be sent to you<br/>
-					<select multiple="multiple" name="host" size="7" style="margin:5px 0px;">
-					<optgroup label="localhost">
-					<option>greenstone 2 mg demo</option>
-					<option>greenstone 2 mgpp demo</option>
-					<option>greenstone 3 mg demo</option>
-					<option selected="selected">any collection on host localhost</option>
-					</optgroup>
-					<optgroup label="nzdl.org">
-					<option>acronym extraction demo</option>
-					<option>music library</option>
-					<option>youth oral history</option>
-					<option>kiwi aircraft images</option>
-					<option selected="selected">any collection on host nzdl.org</option>
-					</optgroup>
+					<select multiple="multiple" name="collection" size="7" style="margin:5px 0px;">
+						#foreach($host in $hostnames)
+						<optgroup label="$host">
+							#foreach($collection in $collectionnames.get($host))
+							<option>$collection</option>
+							#end
+							<option selected="selected">any collection on host $host</option>
+						</optgroup>
+					#end
 					</select></li>
 					<li>You can enter a query in the field below. Only events coming from a collection whose name matches this query will be sent to you.<br/>
@@ -59,5 +54,5 @@
 							</td>
 							<td>
-								<input type="text" name="document_title" style="width:80%;"/>
+								<input type="text" name="collection_query" style="width:80%;" value="$!prefill.collection_query"/>
 							</td>
 						</tr>
Index: /trunk/greenstone3-extensions/gsdl-as/lib/templates/sub_host.vm
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/lib/templates/sub_host.vm	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/lib/templates/sub_host.vm	(revision 8738)
@@ -38,8 +38,9 @@
 					<li>You can choose one or more hosts from the list below. Only events from the host(s) you have chosen will be sent to you.<br/>
 					<select multiple="multiple" name="host" size="3" style="margin:5px 0px;">
-					<option selected="selected">localhost</option>
-					<option>www.greenstone.org</option>
-					<option>www.nzdl.org</option>
-					<option>any host</option>
+					#set($firstoption = true);
+					#foreach($host in $hostnames)
+					<option #if($firstoption) selected="selected" #set($firstoption = false) #end>$host</option>
+					#end
+					<option value="any">any host</option>
 					</select></li>
 					<li>You can enter a query in the field below. Only events coming from a host whose name matches this query will be sent to you.<br/>
@@ -50,5 +51,5 @@
 							</td>
 							<td>
-								<input type="text" name="document_title" style="width:80%;"/>
+								<input type="text" name="host_query" style="width:80%;" value="$!prefill.host_query"/>
 							</td>
 						</tr>
Index: /trunk/greenstone3-extensions/gsdl-as/lib/templates/sub_notification.vm
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/lib/templates/sub_notification.vm	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/lib/templates/sub_notification.vm	(revision 8738)
@@ -37,5 +37,5 @@
 					You will be able to see the subscription in the list of all your subscriptions after logging in to the alerting service. <br/>
 					You can assign a name to your subscription that makes it easier for you to recognise it: 
-					<input name="subscription_name" type="text"/>
+					<input name="subscription_name" type="text" value="$!prefill.subscription_name"/>
 					</div>
 					<br/>
@@ -52,5 +52,5 @@
 							</td>
 							<td>
-								<input type="text" name="email"/>
+								<input type="text" name="email" value="$!prefill.email"/>
 							</td>
 						</tr>
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/AlertingService.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/AlertingService.java	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/AlertingService.java	(revision 8738)
@@ -14,5 +14,4 @@
 import java.util.*;
 
-import javax.mail.Session;
 import javax.servlet.http.*;
 
@@ -20,4 +19,5 @@
 import org.apache.velocity.context.Context;
 import org.apache.velocity.servlet.VelocityServlet;
+import org.greenstone.gsdlas.users.UserManagementException;
 import org.greenstone.gsdlas.users.UserManager;
 
@@ -30,14 +30,19 @@
 public class AlertingService extends VelocityServlet {
     
-    public static final Set actions = new TreeSet();
+    public static final Set actions;
     static {
-        actions.add("createSubscription");
-        actions.add("deleteSubscription");
-        actions.add("editSubscription");
-        actions.add("showFeed");
-        actions.add("listSubscriptions");
-        actions.add("login");
-        actions.add("register");
-        actions.add("logout");
+        Set set = new TreeSet();
+        set.add("createSubscription");
+        set.add("deleteSubscription");
+        set.add("editSubscription");
+        set.add("showFeed");
+        set.add("listSubscriptions");
+        
+        set.add("login");
+        set.add("register");
+        set.add("logout");
+        set.add("showLoginForm");
+        set.add("showRegistrationForm");
+        actions = Collections.unmodifiableSet(set);
     }
     
@@ -67,5 +72,7 @@
         } catch (Exception e) {
             String message = "An error has occured, I couldn't do what you told me to do.";
-            String details = e.getMessage() + " (" + e.getClass().getName() + "), action is " + action;
+            String details = e.getMessage() + " (" + e.getClass().getName() + "); "
+            	+ e.getCause() 
+            	+ " ; action is " + action;
             return showError(context, message, details);
         }
@@ -99,7 +106,8 @@
         if (!UserManager.getInstance().isLoggedIn(session)) {
             session.setAttribute("next_action", "createSubscription");
-            return "login.vm";
+            return showLoginForm(arguments, context);
         }
         if (arguments.containsKey("next_page") && arguments.get("next_page").equals("finish")) {
+            arguments.putAll(getPageArgsFromSession(session));
             ProfileStore.getInstance().createSubscription(arguments);
             return listSubscriptions(arguments, context);
@@ -113,5 +121,5 @@
         if (!UserManager.getInstance().isLoggedIn(session)) {
             session.setAttribute("next_action", "deleteSubscription");
-            return "login.vm";
+            return showLoginForm(arguments, context);
         }
         String subscriptionID = (String) arguments.get("subscriptionID");
@@ -124,5 +132,5 @@
         if (!UserManager.getInstance().isLoggedIn(session)) {
             session.setAttribute("next_action", "editSubscription");
-            return "login.vm";
+            return showLoginForm(arguments, context);
         }
         if (arguments.containsKey("next_page") && arguments.get("next_page").equals("finish")) {
@@ -138,7 +146,21 @@
         return "feed.vm";
     }
+    
+    public String showLoginForm(Map arguments, Context context) {
+        return "login.vm";
+    }
+    
+    public String showRegistrationForm(Map arguments, Context context) {
+        return "register.vm";
+    }
 
     public String listSubscriptions(Map arguments, Context context) {
-        context.put("title", "List of Subscriptions");
+        HttpSession session = ((HttpServletRequest)context.get(REQUEST)).getSession(true);
+        if (!UserManager.getInstance().isLoggedIn(session)) {
+            session.setAttribute("next_action", "listSubscriptions");
+            return showLoginForm(arguments, context);
+        }
+        String username = (String) session.getAttribute("username");
+        context.put("title", "List of Subscriptions for " + username);
         context.put("list", ProfileStore.getInstance().getAllSubscriptions());
         return "list.vm";
@@ -147,5 +169,11 @@
     public String login(Map arguments, Context context) throws Exception {
         HttpSession session = ((HttpServletRequest)context.get(REQUEST)).getSession(true);
-        UserManager.getInstance().loginUser(arguments, session);
+        try {
+            UserManager.getInstance().loginUser(arguments, session);
+        } catch (UserManagementException e) {
+            context.put("error", Boolean.TRUE);
+            context.put("errormessage", e.getMessage());
+            return showLoginForm(arguments, context); 
+        }
         if (session.getAttribute("next_action") != null) {
             String nextAction = (String) session.getAttribute("next_action");
@@ -158,5 +186,11 @@
     public String register(Map arguments, Context context) throws Exception {
         HttpSession session = ((HttpServletRequest)context.get(REQUEST)).getSession(true);
-        UserManager.getInstance().createUser(arguments, session);
+        try {
+            UserManager.getInstance().createUser(arguments, session);
+        } catch (UserManagementException e) {
+            context.put("error", Boolean.TRUE);
+            context.put("errormessage", e.getMessage());
+            return showRegistrationForm(arguments, context);
+        }
         return login(arguments, context);
     }
@@ -194,6 +228,11 @@
         for (Iterator iter = args.keySet().iterator(); iter.hasNext();) {
             String key = (String) iter.next();
-            if (!key.equals("action")) {
-                String firstValue = ((String[]) args.get(key))[0];
+            if (key.equals("type") || key.equals("host") 
+                    || key.equals("collection") || key.equals("way")) {
+                // multi-valued attributes
+                String[] values = ((String[]) args.get(key));
+                result.put(key, Arrays.asList(values));
+            } else {
+                String firstValue = ((String[])args.get(key))[0];
                 result.put(key, firstValue);
             }
@@ -260,7 +299,32 @@
         // get page-specific stuff
         if (nextPage.equals("host")) {
-            
+            GreenstoneCommunicator gsComm = null;
+            String[] hostNames;
+            try {
+                gsComm = new GreenstoneCommunicator();
+                hostNames = gsComm.getHostNames();
+            } catch (Exception e) {
+                hostNames = new String[] { "localhost" };
+            }
+            context.put("hostnames", hostNames);
         } else if (nextPage.equals("collection")) {
-            
+            // TODO might be query instead of just name
+            List hostNames = (List) arguments.get("host");
+            GreenstoneCommunicator gsComm = null;
+            Map collNames = new TreeMap();
+            for (Iterator iter = hostNames.iterator(); iter.hasNext();) {
+                String host = (String) iter.next();
+                Set collNamesForHost = new TreeSet();   
+                try {
+                    gsComm = new GreenstoneCommunicator(new URL("http://" + host + ":8080/soap/servlet/rpcrouter"));
+                    collNamesForHost.addAll(Arrays.asList(gsComm.getCollectionNames()));
+                } catch (Exception e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }	
+                collNames.put(host, collNamesForHost);
+            }
+            context.put("collectionnames", collNames);
+            context.put("hostnames", hostNames);
         }
         
@@ -324,4 +388,16 @@
     }
 
+    private Map getPageArgsFromSession(HttpSession session) {
+        Map result = new TreeMap();
+        Map pageArgs = (Map) session.getAttribute("page_args");
+        if (pageArgs != null) {
+            for (Iterator iter = pageArgs.values().iterator(); iter.hasNext();) {
+                Map args = (Map) iter.next();
+                result.putAll(args);
+            }
+        }
+        return result;
+    }
+    
     /**
      * @param page
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/GreenstoneCommunicator.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/GreenstoneCommunicator.java	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/GreenstoneCommunicator.java	(revision 8738)
@@ -9,5 +9,4 @@
 package org.greenstone.gsdlas;
 
-import java.io.OutputStreamWriter;
 import java.net.URL;
 import java.util.*;
@@ -18,5 +17,4 @@
 import org.apache.soap.Fault;
 import org.apache.soap.rpc.*;
-import org.apache.soap.util.xml.DOM2Writer;
 import org.w3c.dom.*;
 
@@ -44,4 +42,16 @@
     }
 
+    /**
+     * 
+     */
+    public GreenstoneCommunicator() {
+        
+        // TODO Auto-generated constructor stub
+    }
+
+    public String[] getHostNames() {
+        return new String[] {"localhost"};
+    }
+    
     public String[] getCollectionNames() throws Exception {
         Document document = createSubsetDescriptionRequest("", "collectionList");
@@ -56,4 +66,6 @@
             result[i] = name.getNodeValue();
         }
+        
+        System.out.println(Arrays.toString(result));
         
         return result;
@@ -139,5 +151,5 @@
     private Element sendToGreenstone(Element message) throws Exception {
         Call call = new Call();
-        call.setTargetObjectURI("teevee.localsite");
+        call.setTargetObjectURI("localsite");
         call.setMethodName("process");
         // set Encoding Style to use literal XML
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/NotificationStore.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/NotificationStore.java	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/NotificationStore.java	(revision 8738)
@@ -9,9 +9,5 @@
 package org.greenstone.gsdlas;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
 import java.util.*;
-import java.util.Map;
-import java.util.Set;
 
 import org.greenstone.gsdlas.profiles.Subscription;
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/ProfileStore.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/ProfileStore.java	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/ProfileStore.java	(revision 8738)
@@ -14,6 +14,4 @@
 
 import org.greenstone.gsdlas.profiles.*;
-import org.greenstone.gsdlas.profiles.IdEqualsPredicate;
-import org.greenstone.gsdlas.profiles.Subscription;
 
 /**
@@ -33,5 +31,4 @@
 
     private ProfileStore() {
-
     }
 
@@ -206,5 +203,5 @@
         for (Iterator iter = matchedPredicates.iterator(); iter.hasNext();) {
             Predicate pred = (Predicate) iter.next();
-            for (Iterator iterator = pred.getSubscriptions().iterator(); iterator
+            for (Iterator iterator = pred.getSubscriptionIDs().iterator(); iterator
                     .hasNext();) {
                 Subscription sub = (Subscription) iterator.next();
@@ -273,16 +270,8 @@
     /**
      * @param arguments
-     * @throws ParseException
-     */
-    public void createSubscription(Map arguments) throws ParseException {
-        // TODO pass user information to profile store
+     * @throws Exception
+     */
+    public void createSubscription(Map arguments) throws Exception {
         Subscription sub = new Subscription(arguments);
-        // TODO this should probably be somewhere else
-        for (Iterator iter = sub.getPredicates().iterator(); iter.hasNext();) {
-            Predicate predicate = (Predicate) iter.next();
-            if (predicate != null) {
-                predicate.addSubscription(sub);
-            }
-        }
         addSubscription(sub);
     }
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseException.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseException.java	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseException.java	(revision 8738)
@@ -20,3 +20,10 @@
         super(message, cause);
     }
+
+    /**
+     * @param string
+     */
+    public DatabaseException(String message) {
+        super(message);
+    }
 }
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseManager.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseManager.java	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseManager.java	(revision 8738)
@@ -37,8 +37,8 @@
     public Connection getDatabaseConnection() throws DatabaseException {
         if(database == null) {
-            String connectionString = "jdbc:mysql://localhost/alerting";
+            String connectionString = "jdbc:mysql://localhost/alerting?user=gsdl3admin";
             try {
                 Class.forName("com.mysql.jdbc.Driver").newInstance();
-                Connection connection = DriverManager.getConnection(connectionString);
+                database = DriverManager.getConnection(connectionString);
             } catch (Exception e) {
                 throw new DatabaseException("Could not establish connection to database (" + connectionString + ")", e);
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Predicate.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Predicate.java	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Predicate.java	(revision 8738)
@@ -9,7 +9,14 @@
 package org.greenstone.gsdlas.profiles;
 
+import java.sql.*;
+import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.*;
 import java.util.Set;
 import java.util.TreeSet;
+
+import org.greenstone.gsdlas.database.DatabaseException;
+import org.greenstone.gsdlas.database.DatabaseManager;
+import org.greenstone.gsdlas.util.ArrayHelper;
 
 /**
@@ -26,6 +33,19 @@
     protected int id;
     
-    public void addSubscription(Subscription sub) {
-        subscriptions.add(sub);
+
+    public static final String[] singleValueFields = new String[] {
+            "document_title",
+            "document_content",
+            "metadata_has_field",
+            "host_query",
+            "collection_query" };
+    public static final String[] multiValueFields = new String[] {
+            "type",
+            "host",
+            "collection" };
+    
+    public void addSubscription(int subscriptionID) {
+        subscriptions.add(new Integer(subscriptionID));
+        // TODO save to db
     }
 
@@ -33,5 +53,5 @@
      * @see org.greenstone.gsdlas.profiles.Predicate#getSubscriptions()
      */
-    public Set getSubscriptions() {
+    public Set getSubscriptionIDs() {
         return Collections.unmodifiableSet(subscriptions);
     }
@@ -74,4 +94,50 @@
     
     public abstract boolean isSatisfied(Map event);
+
+    /**
+     * @param key
+     * @return
+     */
+    public static boolean isSingleValued(String key) {
+        return ArrayHelper.contains(singleValueFields, key);
+    }
+
+    /**
+     * @param key
+     * @return
+     */
+    public static boolean isMultiValued(String key) {
+        return ArrayHelper.contains(singleValueFields, key);
+    }
+
+    /**
+     * @return
+     * @throws DatabaseException
+     * @throws SQLException
+     */
+    int saveToDatabase() throws SQLException, DatabaseException {
+        // TODO handle multi-valued predicate (in subclass?)
+        String query = "SELECT id FROM predicates " +
+        		"WHERE type='single' AND field = '" + field + 
+        		"' AND singleValue = '" + value + "';";
+        Statement statement = DatabaseManager.getInstance().getDatabaseConnection().createStatement();
+        System.out.println(query);
+        ResultSet result = statement.executeQuery(query);
+        if (result.next()) { // predicate already exists in database
+            System.out.println("predicate " + id + " already exists");
+            return result.getInt("id");
+        }
+        // predicate has been newly created
+        String insert = "INSERT INTO predicates (type, field, singleValue) " +
+        "VALUES ('single', '" + field + "', '" + value + "');";
+        System.out.println(insert);
+        statement.execute(insert);
+        result = statement.executeQuery(query);
+        if (result.next()) {
+            return result.getInt("id");
+        } else {
+            throw new DatabaseException("could not save predicate");
+        }
+    }
     
 }
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/PredicateFactory.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/PredicateFactory.java	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/PredicateFactory.java	(revision 8738)
@@ -9,7 +9,10 @@
 package org.greenstone.gsdlas.profiles;
 
+import java.sql.SQLException;
 import java.util.*;
 import java.util.Map;
 import java.util.TreeMap;
+
+import org.greenstone.gsdlas.database.DatabaseException;
 
 
@@ -42,7 +45,13 @@
      * @return
      * @throws ParseException
+     * @throws DatabaseException
+     * @throws SQLException
      */
     public static Predicate createPredicate(String key, String value)
-            throws ParseException {
+            throws SQLException, DatabaseException {
+        System.out.println("creating predicate for key " + key + " and value " + value);
+        if (!Predicate.isSingleValued(key)) {
+            return null;
+        }
         Predicate result = null;
         if (value == null || value.length() == 0) {
@@ -59,9 +68,16 @@
             result = createSubstringMatchPredicate(key, value);
         } else {
-            throw new ParseException("value " + value + " for field " + key
-                    + " does not lead to a parseable predicate");
+        	return null;
         }
-        result.setID(id++);
+        result.setID(result.saveToDatabase());
         return result;
+    }
+    
+    public static Predicate createPredicate(String key, List values) throws SQLException, DatabaseException {
+        if (!Predicate.isMultiValued(key)) {
+            return null;
+        }
+        // TODO properly handle multi-valued predicates
+        return createPredicate(key, (String) values.get(0));
     }
 
@@ -108,5 +124,5 @@
         return predicate;
     }
-
+    
     /**
      * @param field
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Subscription.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Subscription.java	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Subscription.java	(revision 8738)
@@ -9,7 +9,13 @@
 package org.greenstone.gsdlas.profiles;
 
+import java.sql.*;
+import java.sql.Connection;
+import java.sql.Statement;
 import java.util.*;
 import java.util.Map;
 import java.util.TreeMap;
+
+import org.greenstone.gsdlas.database.DatabaseException;
+import org.greenstone.gsdlas.database.DatabaseManager;
 
 /**
@@ -21,16 +27,29 @@
 public class Subscription implements Comparable {
     private Map map;
+    
     private int id;
+    private String username;
+    private String name;
+    private String email;
+    private boolean rssNotification;
+    private boolean eventsSincePageNotification;
+    
     private int numOfNonEqualsPredicates;
     
-    private static int highestId = 0;
-
-    public Subscription(Map valueMap) throws ParseException {
+    public Subscription(Map valueMap) throws DatabaseException, SQLException {
         map = new TreeMap();
         for (Iterator iter = valueMap.keySet().iterator(); iter.hasNext();) {
             String key = (String) iter.next();
-            String value = (String) valueMap.get(key);
-            Predicate predicate = PredicateFactory.createPredicate(key, value); 
-            map.put(key, predicate);
+            Object value = valueMap.get(key);
+            
+            Predicate predicate = null;
+            if (value instanceof String) {
+                predicate = PredicateFactory.createPredicate(key, (String) value);  
+                map.put(key, predicate);
+            } else if (value instanceof List) {
+                List values = (List)value;
+                predicate = PredicateFactory.createPredicate(key, values);  
+                map.put(key, predicate);
+            } 
             
             if (predicate != null && !(predicate instanceof IdEqualsPredicate)) {
@@ -38,5 +57,20 @@
             }
         }
-        id = highestId++;
+        
+        System.out.println("finished creating predicates");
+        
+        username = (String) valueMap.get("username");
+        name = (String) valueMap.get("subscription_name");
+        email = (String) valueMap.get("email");
+        rssNotification = valueMap.containsKey("way") && ((List)valueMap.get("way")).contains("rss");
+        eventsSincePageNotification = valueMap.containsKey("way") && ((List)valueMap.get("way")).contains("page");
+        id = saveToDatabase(true);
+        
+        for (Iterator iter = getPredicates().iterator(); iter.hasNext();) {
+            Predicate predicate = (Predicate) iter.next();
+            if (predicate != null) {
+                predicate.addSubscription(id);
+            }
+        }
     }
     
@@ -95,3 +129,44 @@
         return new Integer(id).compareTo(new Integer(other.id));
     }
+    
+    private int saveToDatabase(boolean initial) throws DatabaseException, SQLException {
+        Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
+        Statement statement = conn.createStatement();
+        String sqlString;
+        if (initial) {
+            sqlString = "INSERT INTO subscriptions (name, email, rss, page, user) " +
+            		"VALUES ('" + name + "','" + email + "'," + (rssNotification ? 1 : 0) +
+            		"," + (eventsSincePageNotification ? 1: 0)+ ", '" + username + "');";
+        } else {
+            sqlString = "UPDATE subscriptions SET name='" + name + "', email='" +
+            		email + "', rss=" + (rssNotification ? 1 : 0) + ",page=" + 
+            		(eventsSincePageNotification ? 1 : 0)+ "WHERE id=" + id + ";";
+            // cannot change user
+        }
+        System.out.println(sqlString);
+        statement.executeUpdate(sqlString);
+        
+        sqlString = "SELECT id FROM subscriptions WHERE name like '" + name + 
+        			"' AND email like '" + email + "' AND rss=" + (rssNotification ? 1 : 0) 
+        			+ " AND page=" + (eventsSincePageNotification ? 1 : 0) + " AND user like '" +
+        			username + "';";
+        System.out.println(sqlString);
+        ResultSet result = statement.executeQuery(sqlString);
+        int id;
+        if (result.next()) {
+            id = result.getInt("id");
+        } else {
+            throw new DatabaseException("Couldn't save subscription");
+        }
+        
+        if (initial) {
+           for (Iterator iter = getPredicates().iterator(); iter.hasNext();) {
+               Predicate predicate = (Predicate) iter.next();
+               sqlString = "INSERT INTO subs_to_predicates (subscription, predicate) " +
+               		"VALUES (" + id + "," + predicate.getID() + ");";
+               statement.executeUpdate(sqlString);
+           }
+        }
+        return id;
+    }
 }
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/users/PasswordMismatchException.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/users/PasswordMismatchException.java	(revision 8738)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/users/PasswordMismatchException.java	(revision 8738)
@@ -0,0 +1,19 @@
+/*
+ * Created on Dec 3, 2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.greenstone.gsdlas.users;
+
+/**
+ * @author andrea
+ *
+ * TODO To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+public class PasswordMismatchException extends Exception {
+    public PasswordMismatchException(String message) {
+        super(message);
+    }
+}
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManagementException.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManagementException.java	(revision 8738)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManagementException.java	(revision 8738)
@@ -0,0 +1,29 @@
+/*
+ * Created on Dec 3, 2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.greenstone.gsdlas.users;
+
+
+/**
+ * @author andrea
+ *
+ * TODO To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+public class UserManagementException extends Exception {
+
+    /**
+     * @param e
+     */
+    public UserManagementException(String message, Throwable cause) {
+        super(message, cause);
+    }
+    
+    public UserManagementException(String message) {
+        super(message);
+    }
+
+}
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManager.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManager.java	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManager.java	(revision 8738)
@@ -9,7 +9,10 @@
 package org.greenstone.gsdlas.users;
 
+import java.sql.*;
 import java.util.Map;
 
 import javax.servlet.http.HttpSession;
+
+import org.greenstone.gsdlas.database.DatabaseManager;
 
 /**
@@ -40,7 +43,5 @@
         if (session.getCreationTime() - session.getLastAccessedTime() > session.getMaxInactiveInterval())
             return false;
-        
-        // TODO Auto-generated method stub
-        return true;
+        return session.getAttribute("username") != null;
     }
 
@@ -48,7 +49,32 @@
      * @param arguments
      * @param session
+     * @throws PasswordMismatchException
+     * @throws UserManagementException
      */
-    public void createUser(Map arguments, HttpSession session) {
-        // TODO Auto-generated method stub
+    public void createUser(Map arguments, HttpSession session) throws PasswordMismatchException, UserManagementException {
+        if (!arguments.get("password").equals(arguments.get("password2"))) {
+            throw new PasswordMismatchException("The passwords don't match");
+        }
+        String username = (String) arguments.get("username");
+        
+        byte[] password = ((String) arguments.get("password")).getBytes();
+        
+        byte[] pwdHash = password;
+//        try {
+//            pwdHash = MessageDigest.getInstance("MD5").digest(password);
+//        } catch (NoSuchAlgorithmException e) {
+//            e.printStackTrace();
+//            throw new UserManagementException("could not create user", e);
+//        }
+        
+        try {
+            Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
+            Statement statement = conn.createStatement();
+            statement.executeUpdate("INSERT INTO users (username, password) " +
+            		"VALUES ('" + username + "','" + new String(pwdHash) + "')");
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new UserManagementException("could not create user", e);
+        }
         
     }
@@ -57,8 +83,37 @@
      * @param arguments
      * @param session
+     * @throws UserManagementException
      */
-    public void loginUser(Map arguments, HttpSession session) {
-        // TODO Auto-generated method stub
+    public void loginUser(Map arguments, HttpSession session) throws UserManagementException {
+        String username = (String) arguments.get("username");
         
+        if (isLoggedIn(session) && session.getAttribute("username").equals(username)) {
+            return; // already logged in
+        }
+        
+        byte[] password = ((String) arguments.get("password")).getBytes();
+        
+        byte[] pwdHash = password;
+        try {
+//            pwdHash = MessageDigest.getInstance("MD5").digest(password);
+            
+            Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
+            Statement statement = conn.createStatement();
+            ResultSet results = statement.executeQuery("SELECT password " +
+            		"FROM users WHERE username like '" + username + "';");
+            String pwdFromDB = "";
+            if(results.next()) {
+                pwdFromDB = results.getString("password");
+            }
+            if (!pwdFromDB.equals(new String(pwdHash))) {
+                throw new PasswordMismatchException("user " + username 
+                        + " is unknown, or the passwords don't match");
+            }
+            session.setAttribute("username", username);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new UserManagementException("could not login user", e);
+        }
+
     }
     
Index: /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/util/ArrayHelper.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/util/ArrayHelper.java	(revision 8738)
+++ /trunk/greenstone3-extensions/gsdl-as/src/org/greenstone/gsdlas/util/ArrayHelper.java	(revision 8738)
@@ -0,0 +1,26 @@
+/*
+ * Created on Dec 5, 2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.greenstone.gsdlas.util;
+
+import java.util.Arrays;
+
+/**
+ * @author andrea
+ *
+ * TODO To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+public class ArrayHelper {
+    static public boolean contains(String[] array, String element) {
+        if (array == null || element == null) {
+            return false;
+        }
+        Arrays.sort(array);
+        int index = Arrays.binarySearch(array, element);
+        return index > -1 && index <= array.length;
+    }
+}
Index: /trunk/greenstone3-extensions/gsdl-as/test-src/org/greenstone/gsdlas/ProfileStoreTest.java
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/test-src/org/greenstone/gsdlas/ProfileStoreTest.java	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/test-src/org/greenstone/gsdlas/ProfileStoreTest.java	(revision 8738)
@@ -13,6 +13,4 @@
 
 import junit.framework.TestCase;
-
-import org.greenstone.gsdlas.profiles.*;
 
 /**
@@ -31,5 +29,5 @@
         try {
             addSubscriptions();
-        } catch (ParseException e) {
+        } catch (Exception e) {
             e.printStackTrace();
             fail(e.getMessage());
@@ -60,5 +58,5 @@
     }
 
-    private void addSubscriptions() throws ParseException {
+    private void addSubscriptions() throws Exception {
         Map valueMap = new TreeMap();
         
@@ -83,5 +81,5 @@
     }
     
-    private void addSubscription(Map valueMap) throws ParseException {  
+    private void addSubscription(Map valueMap) throws Exception {  
         ProfileStore.getInstance().createSubscription(valueMap);    
     }
Index: /trunk/greenstone3-extensions/gsdl-as/web/form.css
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/web/form.css	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/web/form.css	(revision 8738)
@@ -40,4 +40,5 @@
 	color: #555;
 	border-top: 10px solid #CCC;
+	font-family: "Trebuchet MS", Arial, helvetica, sans-serif;
 }
 
@@ -49,5 +50,4 @@
 #progress td.current {
 	background-color: #F0E68C;
-	font-style: italic;
 	color: #000;
 }
Index: /trunk/greenstone3-extensions/gsdl-as/web/index.html
===================================================================
--- /trunk/greenstone3-extensions/gsdl-as/web/index.html	(revision 8737)
+++ /trunk/greenstone3-extensions/gsdl-as/web/index.html	(revision 8738)
@@ -17,9 +17,14 @@
 			<ul>
 				<li>
+					<a href="/alerting/service?action=showLoginForm">Log in</a></li>
+				</li>
+				<li>
 					<a href="subscribe.html">Create</a> a subscription.</li>
 				<li>
-					<a href="service?action=listSubscriptions">View and edit</a> your existing subscriptions.</li>
+					<a href="/alerting/service?action=listSubscriptions">View and edit</a> your existing subscriptions.</li>
 				<li>
 					<a href="about.html">Read more</a> about the alerting service.</li>
+				<li>
+					<a href="/alerting/service?action=logout">Log out</a></li?
 			</ul>
 		</div>
Index: /trunk/gsdl3/extensions/gsdl-as/build.xml
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/build.xml	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/build.xml	(revision 8738)
@@ -217,7 +217,7 @@
     <!-- Include all JAR files that will be included in /WEB-INF/lib -->
     <!-- *** CUSTOMIZE HERE AS REQUIRED BY YOUR APPLICATION *** -->
-<!--
-    <pathelement location="${foo.jar}"/>
--->
+    <fileset dir="lib">
+    	<include name="*.jar"/>
+    </fileset>
 
     <!-- Include all elements that Tomcat exposes to applications -->
Index: /trunk/gsdl3/extensions/gsdl-as/lib/templates/login.vm
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/lib/templates/login.vm	(revision 8738)
+++ /trunk/gsdl3/extensions/gsdl-as/lib/templates/login.vm	(revision 8738)
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Greenstone Digital Library Software</title>
+<link href="/alerting/form.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="content">
+<h3>Alerting Service for Greenstone</h3>
+<p>Login</p>
+#if ($error) 
+<div class="error">
+An error occured:<br/>
+$errormessage
+</div>
+#end
+<form action="/alerting/service?action=login" method="post"/>
+<table>
+<tr>
+<th><label for="username">username</label></th><td><input type="text" name="username" id="username"/></td>
+</tr>
+<tr>
+<th><label for="password">password</label></th><td><input type="password" name="password" id="password"/></td>
+</tr>
+</table>
+<br/>
+<input type="submit" value="login"/>
+</form>
+<div class="description">Don't have an account yet? <a href="/alerting/service?action=showRegistrationForm">Register</a> now!</a></div>
+</div>
+
+</body>
+</html>
Index: /trunk/gsdl3/extensions/gsdl-as/lib/templates/register.vm
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/lib/templates/register.vm	(revision 8738)
+++ /trunk/gsdl3/extensions/gsdl-as/lib/templates/register.vm	(revision 8738)
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Greenstone Digital Library Software</title>
+<link href="/alerting/form.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="content">
+<h3>Alerting Service for Greenstone</h3>
+<p>Register</p>
+#if ($error)
+<div class="error">
+An error occured:<br/>
+$errormessage
+</div>
+#end
+<form action="/alerting/service?action=register" method="post"/>
+<table>
+<tr>
+<th><label for="username">username</label></th><td><input type="text" name="username" id="username"/></td>
+</tr>
+<tr>
+<th><label for="password">password</label></th><td><input type="password" name="password" id="password"/></td>
+</tr>
+<tr>
+<th><label for="password2">repeat password</label></th><td><input type="password" name="password2" id="password2"/></td>
+</tr>
+</table>
+<br/>
+<input type="submit" value="create account"/>
+</form>
+</div>
+
+</body>
+</html>
Index: /trunk/gsdl3/extensions/gsdl-as/lib/templates/sub_collection.vm
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/lib/templates/sub_collection.vm	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/lib/templates/sub_collection.vm	(revision 8738)
@@ -37,18 +37,13 @@
 					<ol>
 					<li>You can choose one or more collections from the list below. Only events from the collections(s) you have chosen will be sent to you<br/>
-					<select multiple="multiple" name="host" size="7" style="margin:5px 0px;">
-					<optgroup label="localhost">
-					<option>greenstone 2 mg demo</option>
-					<option>greenstone 2 mgpp demo</option>
-					<option>greenstone 3 mg demo</option>
-					<option selected="selected">any collection on host localhost</option>
-					</optgroup>
-					<optgroup label="nzdl.org">
-					<option>acronym extraction demo</option>
-					<option>music library</option>
-					<option>youth oral history</option>
-					<option>kiwi aircraft images</option>
-					<option selected="selected">any collection on host nzdl.org</option>
-					</optgroup>
+					<select multiple="multiple" name="collection" size="7" style="margin:5px 0px;">
+						#foreach($host in $hostnames)
+						<optgroup label="$host">
+							#foreach($collection in $collectionnames.get($host))
+							<option>$collection</option>
+							#end
+							<option selected="selected">any collection on host $host</option>
+						</optgroup>
+					#end
 					</select></li>
 					<li>You can enter a query in the field below. Only events coming from a collection whose name matches this query will be sent to you.<br/>
@@ -59,5 +54,5 @@
 							</td>
 							<td>
-								<input type="text" name="document_title" style="width:80%;"/>
+								<input type="text" name="collection_query" style="width:80%;" value="$!prefill.collection_query"/>
 							</td>
 						</tr>
Index: /trunk/gsdl3/extensions/gsdl-as/lib/templates/sub_host.vm
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/lib/templates/sub_host.vm	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/lib/templates/sub_host.vm	(revision 8738)
@@ -38,8 +38,9 @@
 					<li>You can choose one or more hosts from the list below. Only events from the host(s) you have chosen will be sent to you.<br/>
 					<select multiple="multiple" name="host" size="3" style="margin:5px 0px;">
-					<option selected="selected">localhost</option>
-					<option>www.greenstone.org</option>
-					<option>www.nzdl.org</option>
-					<option>any host</option>
+					#set($firstoption = true);
+					#foreach($host in $hostnames)
+					<option #if($firstoption) selected="selected" #set($firstoption = false) #end>$host</option>
+					#end
+					<option value="any">any host</option>
 					</select></li>
 					<li>You can enter a query in the field below. Only events coming from a host whose name matches this query will be sent to you.<br/>
@@ -50,5 +51,5 @@
 							</td>
 							<td>
-								<input type="text" name="document_title" style="width:80%;"/>
+								<input type="text" name="host_query" style="width:80%;" value="$!prefill.host_query"/>
 							</td>
 						</tr>
Index: /trunk/gsdl3/extensions/gsdl-as/lib/templates/sub_notification.vm
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/lib/templates/sub_notification.vm	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/lib/templates/sub_notification.vm	(revision 8738)
@@ -37,5 +37,5 @@
 					You will be able to see the subscription in the list of all your subscriptions after logging in to the alerting service. <br/>
 					You can assign a name to your subscription that makes it easier for you to recognise it: 
-					<input name="subscription_name" type="text"/>
+					<input name="subscription_name" type="text" value="$!prefill.subscription_name"/>
 					</div>
 					<br/>
@@ -52,5 +52,5 @@
 							</td>
 							<td>
-								<input type="text" name="email"/>
+								<input type="text" name="email" value="$!prefill.email"/>
 							</td>
 						</tr>
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/AlertingService.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/AlertingService.java	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/AlertingService.java	(revision 8738)
@@ -14,5 +14,4 @@
 import java.util.*;
 
-import javax.mail.Session;
 import javax.servlet.http.*;
 
@@ -20,4 +19,5 @@
 import org.apache.velocity.context.Context;
 import org.apache.velocity.servlet.VelocityServlet;
+import org.greenstone.gsdlas.users.UserManagementException;
 import org.greenstone.gsdlas.users.UserManager;
 
@@ -30,14 +30,19 @@
 public class AlertingService extends VelocityServlet {
     
-    public static final Set actions = new TreeSet();
+    public static final Set actions;
     static {
-        actions.add("createSubscription");
-        actions.add("deleteSubscription");
-        actions.add("editSubscription");
-        actions.add("showFeed");
-        actions.add("listSubscriptions");
-        actions.add("login");
-        actions.add("register");
-        actions.add("logout");
+        Set set = new TreeSet();
+        set.add("createSubscription");
+        set.add("deleteSubscription");
+        set.add("editSubscription");
+        set.add("showFeed");
+        set.add("listSubscriptions");
+        
+        set.add("login");
+        set.add("register");
+        set.add("logout");
+        set.add("showLoginForm");
+        set.add("showRegistrationForm");
+        actions = Collections.unmodifiableSet(set);
     }
     
@@ -67,5 +72,7 @@
         } catch (Exception e) {
             String message = "An error has occured, I couldn't do what you told me to do.";
-            String details = e.getMessage() + " (" + e.getClass().getName() + "), action is " + action;
+            String details = e.getMessage() + " (" + e.getClass().getName() + "); "
+            	+ e.getCause() 
+            	+ " ; action is " + action;
             return showError(context, message, details);
         }
@@ -99,7 +106,8 @@
         if (!UserManager.getInstance().isLoggedIn(session)) {
             session.setAttribute("next_action", "createSubscription");
-            return "login.vm";
+            return showLoginForm(arguments, context);
         }
         if (arguments.containsKey("next_page") && arguments.get("next_page").equals("finish")) {
+            arguments.putAll(getPageArgsFromSession(session));
             ProfileStore.getInstance().createSubscription(arguments);
             return listSubscriptions(arguments, context);
@@ -113,5 +121,5 @@
         if (!UserManager.getInstance().isLoggedIn(session)) {
             session.setAttribute("next_action", "deleteSubscription");
-            return "login.vm";
+            return showLoginForm(arguments, context);
         }
         String subscriptionID = (String) arguments.get("subscriptionID");
@@ -124,5 +132,5 @@
         if (!UserManager.getInstance().isLoggedIn(session)) {
             session.setAttribute("next_action", "editSubscription");
-            return "login.vm";
+            return showLoginForm(arguments, context);
         }
         if (arguments.containsKey("next_page") && arguments.get("next_page").equals("finish")) {
@@ -138,7 +146,21 @@
         return "feed.vm";
     }
+    
+    public String showLoginForm(Map arguments, Context context) {
+        return "login.vm";
+    }
+    
+    public String showRegistrationForm(Map arguments, Context context) {
+        return "register.vm";
+    }
 
     public String listSubscriptions(Map arguments, Context context) {
-        context.put("title", "List of Subscriptions");
+        HttpSession session = ((HttpServletRequest)context.get(REQUEST)).getSession(true);
+        if (!UserManager.getInstance().isLoggedIn(session)) {
+            session.setAttribute("next_action", "listSubscriptions");
+            return showLoginForm(arguments, context);
+        }
+        String username = (String) session.getAttribute("username");
+        context.put("title", "List of Subscriptions for " + username);
         context.put("list", ProfileStore.getInstance().getAllSubscriptions());
         return "list.vm";
@@ -147,5 +169,11 @@
     public String login(Map arguments, Context context) throws Exception {
         HttpSession session = ((HttpServletRequest)context.get(REQUEST)).getSession(true);
-        UserManager.getInstance().loginUser(arguments, session);
+        try {
+            UserManager.getInstance().loginUser(arguments, session);
+        } catch (UserManagementException e) {
+            context.put("error", Boolean.TRUE);
+            context.put("errormessage", e.getMessage());
+            return showLoginForm(arguments, context); 
+        }
         if (session.getAttribute("next_action") != null) {
             String nextAction = (String) session.getAttribute("next_action");
@@ -158,5 +186,11 @@
     public String register(Map arguments, Context context) throws Exception {
         HttpSession session = ((HttpServletRequest)context.get(REQUEST)).getSession(true);
-        UserManager.getInstance().createUser(arguments, session);
+        try {
+            UserManager.getInstance().createUser(arguments, session);
+        } catch (UserManagementException e) {
+            context.put("error", Boolean.TRUE);
+            context.put("errormessage", e.getMessage());
+            return showRegistrationForm(arguments, context);
+        }
         return login(arguments, context);
     }
@@ -194,6 +228,11 @@
         for (Iterator iter = args.keySet().iterator(); iter.hasNext();) {
             String key = (String) iter.next();
-            if (!key.equals("action")) {
-                String firstValue = ((String[]) args.get(key))[0];
+            if (key.equals("type") || key.equals("host") 
+                    || key.equals("collection") || key.equals("way")) {
+                // multi-valued attributes
+                String[] values = ((String[]) args.get(key));
+                result.put(key, Arrays.asList(values));
+            } else {
+                String firstValue = ((String[])args.get(key))[0];
                 result.put(key, firstValue);
             }
@@ -260,7 +299,32 @@
         // get page-specific stuff
         if (nextPage.equals("host")) {
-            
+            GreenstoneCommunicator gsComm = null;
+            String[] hostNames;
+            try {
+                gsComm = new GreenstoneCommunicator();
+                hostNames = gsComm.getHostNames();
+            } catch (Exception e) {
+                hostNames = new String[] { "localhost" };
+            }
+            context.put("hostnames", hostNames);
         } else if (nextPage.equals("collection")) {
-            
+            // TODO might be query instead of just name
+            List hostNames = (List) arguments.get("host");
+            GreenstoneCommunicator gsComm = null;
+            Map collNames = new TreeMap();
+            for (Iterator iter = hostNames.iterator(); iter.hasNext();) {
+                String host = (String) iter.next();
+                Set collNamesForHost = new TreeSet();   
+                try {
+                    gsComm = new GreenstoneCommunicator(new URL("http://" + host + ":8080/soap/servlet/rpcrouter"));
+                    collNamesForHost.addAll(Arrays.asList(gsComm.getCollectionNames()));
+                } catch (Exception e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }	
+                collNames.put(host, collNamesForHost);
+            }
+            context.put("collectionnames", collNames);
+            context.put("hostnames", hostNames);
         }
         
@@ -324,4 +388,16 @@
     }
 
+    private Map getPageArgsFromSession(HttpSession session) {
+        Map result = new TreeMap();
+        Map pageArgs = (Map) session.getAttribute("page_args");
+        if (pageArgs != null) {
+            for (Iterator iter = pageArgs.values().iterator(); iter.hasNext();) {
+                Map args = (Map) iter.next();
+                result.putAll(args);
+            }
+        }
+        return result;
+    }
+    
     /**
      * @param page
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/GreenstoneCommunicator.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/GreenstoneCommunicator.java	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/GreenstoneCommunicator.java	(revision 8738)
@@ -9,5 +9,4 @@
 package org.greenstone.gsdlas;
 
-import java.io.OutputStreamWriter;
 import java.net.URL;
 import java.util.*;
@@ -18,5 +17,4 @@
 import org.apache.soap.Fault;
 import org.apache.soap.rpc.*;
-import org.apache.soap.util.xml.DOM2Writer;
 import org.w3c.dom.*;
 
@@ -44,4 +42,16 @@
     }
 
+    /**
+     * 
+     */
+    public GreenstoneCommunicator() {
+        
+        // TODO Auto-generated constructor stub
+    }
+
+    public String[] getHostNames() {
+        return new String[] {"localhost"};
+    }
+    
     public String[] getCollectionNames() throws Exception {
         Document document = createSubsetDescriptionRequest("", "collectionList");
@@ -56,4 +66,6 @@
             result[i] = name.getNodeValue();
         }
+        
+        System.out.println(Arrays.toString(result));
         
         return result;
@@ -139,5 +151,5 @@
     private Element sendToGreenstone(Element message) throws Exception {
         Call call = new Call();
-        call.setTargetObjectURI("teevee.localsite");
+        call.setTargetObjectURI("localsite");
         call.setMethodName("process");
         // set Encoding Style to use literal XML
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/NotificationStore.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/NotificationStore.java	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/NotificationStore.java	(revision 8738)
@@ -9,9 +9,5 @@
 package org.greenstone.gsdlas;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
 import java.util.*;
-import java.util.Map;
-import java.util.Set;
 
 import org.greenstone.gsdlas.profiles.Subscription;
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/ProfileStore.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/ProfileStore.java	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/ProfileStore.java	(revision 8738)
@@ -14,6 +14,4 @@
 
 import org.greenstone.gsdlas.profiles.*;
-import org.greenstone.gsdlas.profiles.IdEqualsPredicate;
-import org.greenstone.gsdlas.profiles.Subscription;
 
 /**
@@ -33,5 +31,4 @@
 
     private ProfileStore() {
-
     }
 
@@ -206,5 +203,5 @@
         for (Iterator iter = matchedPredicates.iterator(); iter.hasNext();) {
             Predicate pred = (Predicate) iter.next();
-            for (Iterator iterator = pred.getSubscriptions().iterator(); iterator
+            for (Iterator iterator = pred.getSubscriptionIDs().iterator(); iterator
                     .hasNext();) {
                 Subscription sub = (Subscription) iterator.next();
@@ -273,16 +270,8 @@
     /**
      * @param arguments
-     * @throws ParseException
-     */
-    public void createSubscription(Map arguments) throws ParseException {
-        // TODO pass user information to profile store
+     * @throws Exception
+     */
+    public void createSubscription(Map arguments) throws Exception {
         Subscription sub = new Subscription(arguments);
-        // TODO this should probably be somewhere else
-        for (Iterator iter = sub.getPredicates().iterator(); iter.hasNext();) {
-            Predicate predicate = (Predicate) iter.next();
-            if (predicate != null) {
-                predicate.addSubscription(sub);
-            }
-        }
         addSubscription(sub);
     }
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseException.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseException.java	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseException.java	(revision 8738)
@@ -20,3 +20,10 @@
         super(message, cause);
     }
+
+    /**
+     * @param string
+     */
+    public DatabaseException(String message) {
+        super(message);
+    }
 }
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseManager.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseManager.java	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseManager.java	(revision 8738)
@@ -37,8 +37,8 @@
     public Connection getDatabaseConnection() throws DatabaseException {
         if(database == null) {
-            String connectionString = "jdbc:mysql://localhost/alerting";
+            String connectionString = "jdbc:mysql://localhost/alerting?user=gsdl3admin";
             try {
                 Class.forName("com.mysql.jdbc.Driver").newInstance();
-                Connection connection = DriverManager.getConnection(connectionString);
+                database = DriverManager.getConnection(connectionString);
             } catch (Exception e) {
                 throw new DatabaseException("Could not establish connection to database (" + connectionString + ")", e);
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Predicate.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Predicate.java	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Predicate.java	(revision 8738)
@@ -9,7 +9,14 @@
 package org.greenstone.gsdlas.profiles;
 
+import java.sql.*;
+import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.*;
 import java.util.Set;
 import java.util.TreeSet;
+
+import org.greenstone.gsdlas.database.DatabaseException;
+import org.greenstone.gsdlas.database.DatabaseManager;
+import org.greenstone.gsdlas.util.ArrayHelper;
 
 /**
@@ -26,6 +33,19 @@
     protected int id;
     
-    public void addSubscription(Subscription sub) {
-        subscriptions.add(sub);
+
+    public static final String[] singleValueFields = new String[] {
+            "document_title",
+            "document_content",
+            "metadata_has_field",
+            "host_query",
+            "collection_query" };
+    public static final String[] multiValueFields = new String[] {
+            "type",
+            "host",
+            "collection" };
+    
+    public void addSubscription(int subscriptionID) {
+        subscriptions.add(new Integer(subscriptionID));
+        // TODO save to db
     }
 
@@ -33,5 +53,5 @@
      * @see org.greenstone.gsdlas.profiles.Predicate#getSubscriptions()
      */
-    public Set getSubscriptions() {
+    public Set getSubscriptionIDs() {
         return Collections.unmodifiableSet(subscriptions);
     }
@@ -74,4 +94,50 @@
     
     public abstract boolean isSatisfied(Map event);
+
+    /**
+     * @param key
+     * @return
+     */
+    public static boolean isSingleValued(String key) {
+        return ArrayHelper.contains(singleValueFields, key);
+    }
+
+    /**
+     * @param key
+     * @return
+     */
+    public static boolean isMultiValued(String key) {
+        return ArrayHelper.contains(singleValueFields, key);
+    }
+
+    /**
+     * @return
+     * @throws DatabaseException
+     * @throws SQLException
+     */
+    int saveToDatabase() throws SQLException, DatabaseException {
+        // TODO handle multi-valued predicate (in subclass?)
+        String query = "SELECT id FROM predicates " +
+        		"WHERE type='single' AND field = '" + field + 
+        		"' AND singleValue = '" + value + "';";
+        Statement statement = DatabaseManager.getInstance().getDatabaseConnection().createStatement();
+        System.out.println(query);
+        ResultSet result = statement.executeQuery(query);
+        if (result.next()) { // predicate already exists in database
+            System.out.println("predicate " + id + " already exists");
+            return result.getInt("id");
+        }
+        // predicate has been newly created
+        String insert = "INSERT INTO predicates (type, field, singleValue) " +
+        "VALUES ('single', '" + field + "', '" + value + "');";
+        System.out.println(insert);
+        statement.execute(insert);
+        result = statement.executeQuery(query);
+        if (result.next()) {
+            return result.getInt("id");
+        } else {
+            throw new DatabaseException("could not save predicate");
+        }
+    }
     
 }
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/PredicateFactory.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/PredicateFactory.java	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/PredicateFactory.java	(revision 8738)
@@ -9,7 +9,10 @@
 package org.greenstone.gsdlas.profiles;
 
+import java.sql.SQLException;
 import java.util.*;
 import java.util.Map;
 import java.util.TreeMap;
+
+import org.greenstone.gsdlas.database.DatabaseException;
 
 
@@ -42,7 +45,13 @@
      * @return
      * @throws ParseException
+     * @throws DatabaseException
+     * @throws SQLException
      */
     public static Predicate createPredicate(String key, String value)
-            throws ParseException {
+            throws SQLException, DatabaseException {
+        System.out.println("creating predicate for key " + key + " and value " + value);
+        if (!Predicate.isSingleValued(key)) {
+            return null;
+        }
         Predicate result = null;
         if (value == null || value.length() == 0) {
@@ -59,9 +68,16 @@
             result = createSubstringMatchPredicate(key, value);
         } else {
-            throw new ParseException("value " + value + " for field " + key
-                    + " does not lead to a parseable predicate");
+        	return null;
         }
-        result.setID(id++);
+        result.setID(result.saveToDatabase());
         return result;
+    }
+    
+    public static Predicate createPredicate(String key, List values) throws SQLException, DatabaseException {
+        if (!Predicate.isMultiValued(key)) {
+            return null;
+        }
+        // TODO properly handle multi-valued predicates
+        return createPredicate(key, (String) values.get(0));
     }
 
@@ -108,5 +124,5 @@
         return predicate;
     }
-
+    
     /**
      * @param field
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Subscription.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Subscription.java	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/profiles/Subscription.java	(revision 8738)
@@ -9,7 +9,13 @@
 package org.greenstone.gsdlas.profiles;
 
+import java.sql.*;
+import java.sql.Connection;
+import java.sql.Statement;
 import java.util.*;
 import java.util.Map;
 import java.util.TreeMap;
+
+import org.greenstone.gsdlas.database.DatabaseException;
+import org.greenstone.gsdlas.database.DatabaseManager;
 
 /**
@@ -21,16 +27,29 @@
 public class Subscription implements Comparable {
     private Map map;
+    
     private int id;
+    private String username;
+    private String name;
+    private String email;
+    private boolean rssNotification;
+    private boolean eventsSincePageNotification;
+    
     private int numOfNonEqualsPredicates;
     
-    private static int highestId = 0;
-
-    public Subscription(Map valueMap) throws ParseException {
+    public Subscription(Map valueMap) throws DatabaseException, SQLException {
         map = new TreeMap();
         for (Iterator iter = valueMap.keySet().iterator(); iter.hasNext();) {
             String key = (String) iter.next();
-            String value = (String) valueMap.get(key);
-            Predicate predicate = PredicateFactory.createPredicate(key, value); 
-            map.put(key, predicate);
+            Object value = valueMap.get(key);
+            
+            Predicate predicate = null;
+            if (value instanceof String) {
+                predicate = PredicateFactory.createPredicate(key, (String) value);  
+                map.put(key, predicate);
+            } else if (value instanceof List) {
+                List values = (List)value;
+                predicate = PredicateFactory.createPredicate(key, values);  
+                map.put(key, predicate);
+            } 
             
             if (predicate != null && !(predicate instanceof IdEqualsPredicate)) {
@@ -38,5 +57,20 @@
             }
         }
-        id = highestId++;
+        
+        System.out.println("finished creating predicates");
+        
+        username = (String) valueMap.get("username");
+        name = (String) valueMap.get("subscription_name");
+        email = (String) valueMap.get("email");
+        rssNotification = valueMap.containsKey("way") && ((List)valueMap.get("way")).contains("rss");
+        eventsSincePageNotification = valueMap.containsKey("way") && ((List)valueMap.get("way")).contains("page");
+        id = saveToDatabase(true);
+        
+        for (Iterator iter = getPredicates().iterator(); iter.hasNext();) {
+            Predicate predicate = (Predicate) iter.next();
+            if (predicate != null) {
+                predicate.addSubscription(id);
+            }
+        }
     }
     
@@ -95,3 +129,44 @@
         return new Integer(id).compareTo(new Integer(other.id));
     }
+    
+    private int saveToDatabase(boolean initial) throws DatabaseException, SQLException {
+        Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
+        Statement statement = conn.createStatement();
+        String sqlString;
+        if (initial) {
+            sqlString = "INSERT INTO subscriptions (name, email, rss, page, user) " +
+            		"VALUES ('" + name + "','" + email + "'," + (rssNotification ? 1 : 0) +
+            		"," + (eventsSincePageNotification ? 1: 0)+ ", '" + username + "');";
+        } else {
+            sqlString = "UPDATE subscriptions SET name='" + name + "', email='" +
+            		email + "', rss=" + (rssNotification ? 1 : 0) + ",page=" + 
+            		(eventsSincePageNotification ? 1 : 0)+ "WHERE id=" + id + ";";
+            // cannot change user
+        }
+        System.out.println(sqlString);
+        statement.executeUpdate(sqlString);
+        
+        sqlString = "SELECT id FROM subscriptions WHERE name like '" + name + 
+        			"' AND email like '" + email + "' AND rss=" + (rssNotification ? 1 : 0) 
+        			+ " AND page=" + (eventsSincePageNotification ? 1 : 0) + " AND user like '" +
+        			username + "';";
+        System.out.println(sqlString);
+        ResultSet result = statement.executeQuery(sqlString);
+        int id;
+        if (result.next()) {
+            id = result.getInt("id");
+        } else {
+            throw new DatabaseException("Couldn't save subscription");
+        }
+        
+        if (initial) {
+           for (Iterator iter = getPredicates().iterator(); iter.hasNext();) {
+               Predicate predicate = (Predicate) iter.next();
+               sqlString = "INSERT INTO subs_to_predicates (subscription, predicate) " +
+               		"VALUES (" + id + "," + predicate.getID() + ");";
+               statement.executeUpdate(sqlString);
+           }
+        }
+        return id;
+    }
 }
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/users/PasswordMismatchException.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/users/PasswordMismatchException.java	(revision 8738)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/users/PasswordMismatchException.java	(revision 8738)
@@ -0,0 +1,19 @@
+/*
+ * Created on Dec 3, 2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.greenstone.gsdlas.users;
+
+/**
+ * @author andrea
+ *
+ * TODO To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+public class PasswordMismatchException extends Exception {
+    public PasswordMismatchException(String message) {
+        super(message);
+    }
+}
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManagementException.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManagementException.java	(revision 8738)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManagementException.java	(revision 8738)
@@ -0,0 +1,29 @@
+/*
+ * Created on Dec 3, 2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.greenstone.gsdlas.users;
+
+
+/**
+ * @author andrea
+ *
+ * TODO To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+public class UserManagementException extends Exception {
+
+    /**
+     * @param e
+     */
+    public UserManagementException(String message, Throwable cause) {
+        super(message, cause);
+    }
+    
+    public UserManagementException(String message) {
+        super(message);
+    }
+
+}
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManager.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManager.java	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/users/UserManager.java	(revision 8738)
@@ -9,7 +9,10 @@
 package org.greenstone.gsdlas.users;
 
+import java.sql.*;
 import java.util.Map;
 
 import javax.servlet.http.HttpSession;
+
+import org.greenstone.gsdlas.database.DatabaseManager;
 
 /**
@@ -40,7 +43,5 @@
         if (session.getCreationTime() - session.getLastAccessedTime() > session.getMaxInactiveInterval())
             return false;
-        
-        // TODO Auto-generated method stub
-        return true;
+        return session.getAttribute("username") != null;
     }
 
@@ -48,7 +49,32 @@
      * @param arguments
      * @param session
+     * @throws PasswordMismatchException
+     * @throws UserManagementException
      */
-    public void createUser(Map arguments, HttpSession session) {
-        // TODO Auto-generated method stub
+    public void createUser(Map arguments, HttpSession session) throws PasswordMismatchException, UserManagementException {
+        if (!arguments.get("password").equals(arguments.get("password2"))) {
+            throw new PasswordMismatchException("The passwords don't match");
+        }
+        String username = (String) arguments.get("username");
+        
+        byte[] password = ((String) arguments.get("password")).getBytes();
+        
+        byte[] pwdHash = password;
+//        try {
+//            pwdHash = MessageDigest.getInstance("MD5").digest(password);
+//        } catch (NoSuchAlgorithmException e) {
+//            e.printStackTrace();
+//            throw new UserManagementException("could not create user", e);
+//        }
+        
+        try {
+            Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
+            Statement statement = conn.createStatement();
+            statement.executeUpdate("INSERT INTO users (username, password) " +
+            		"VALUES ('" + username + "','" + new String(pwdHash) + "')");
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new UserManagementException("could not create user", e);
+        }
         
     }
@@ -57,8 +83,37 @@
      * @param arguments
      * @param session
+     * @throws UserManagementException
      */
-    public void loginUser(Map arguments, HttpSession session) {
-        // TODO Auto-generated method stub
+    public void loginUser(Map arguments, HttpSession session) throws UserManagementException {
+        String username = (String) arguments.get("username");
         
+        if (isLoggedIn(session) && session.getAttribute("username").equals(username)) {
+            return; // already logged in
+        }
+        
+        byte[] password = ((String) arguments.get("password")).getBytes();
+        
+        byte[] pwdHash = password;
+        try {
+//            pwdHash = MessageDigest.getInstance("MD5").digest(password);
+            
+            Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
+            Statement statement = conn.createStatement();
+            ResultSet results = statement.executeQuery("SELECT password " +
+            		"FROM users WHERE username like '" + username + "';");
+            String pwdFromDB = "";
+            if(results.next()) {
+                pwdFromDB = results.getString("password");
+            }
+            if (!pwdFromDB.equals(new String(pwdHash))) {
+                throw new PasswordMismatchException("user " + username 
+                        + " is unknown, or the passwords don't match");
+            }
+            session.setAttribute("username", username);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new UserManagementException("could not login user", e);
+        }
+
     }
     
Index: /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/util/ArrayHelper.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/util/ArrayHelper.java	(revision 8738)
+++ /trunk/gsdl3/extensions/gsdl-as/src/org/greenstone/gsdlas/util/ArrayHelper.java	(revision 8738)
@@ -0,0 +1,26 @@
+/*
+ * Created on Dec 5, 2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.greenstone.gsdlas.util;
+
+import java.util.Arrays;
+
+/**
+ * @author andrea
+ *
+ * TODO To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+public class ArrayHelper {
+    static public boolean contains(String[] array, String element) {
+        if (array == null || element == null) {
+            return false;
+        }
+        Arrays.sort(array);
+        int index = Arrays.binarySearch(array, element);
+        return index > -1 && index <= array.length;
+    }
+}
Index: /trunk/gsdl3/extensions/gsdl-as/test-src/org/greenstone/gsdlas/ProfileStoreTest.java
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/test-src/org/greenstone/gsdlas/ProfileStoreTest.java	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/test-src/org/greenstone/gsdlas/ProfileStoreTest.java	(revision 8738)
@@ -13,6 +13,4 @@
 
 import junit.framework.TestCase;
-
-import org.greenstone.gsdlas.profiles.*;
 
 /**
@@ -31,5 +29,5 @@
         try {
             addSubscriptions();
-        } catch (ParseException e) {
+        } catch (Exception e) {
             e.printStackTrace();
             fail(e.getMessage());
@@ -60,5 +58,5 @@
     }
 
-    private void addSubscriptions() throws ParseException {
+    private void addSubscriptions() throws Exception {
         Map valueMap = new TreeMap();
         
@@ -83,5 +81,5 @@
     }
     
-    private void addSubscription(Map valueMap) throws ParseException {  
+    private void addSubscription(Map valueMap) throws Exception {  
         ProfileStore.getInstance().createSubscription(valueMap);    
     }
Index: /trunk/gsdl3/extensions/gsdl-as/web/form.css
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/web/form.css	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/web/form.css	(revision 8738)
@@ -40,4 +40,5 @@
 	color: #555;
 	border-top: 10px solid #CCC;
+	font-family: "Trebuchet MS", Arial, helvetica, sans-serif;
 }
 
@@ -49,5 +50,4 @@
 #progress td.current {
 	background-color: #F0E68C;
-	font-style: italic;
 	color: #000;
 }
Index: /trunk/gsdl3/extensions/gsdl-as/web/index.html
===================================================================
--- /trunk/gsdl3/extensions/gsdl-as/web/index.html	(revision 8737)
+++ /trunk/gsdl3/extensions/gsdl-as/web/index.html	(revision 8738)
@@ -17,9 +17,14 @@
 			<ul>
 				<li>
+					<a href="/alerting/service?action=showLoginForm">Log in</a></li>
+				</li>
+				<li>
 					<a href="subscribe.html">Create</a> a subscription.</li>
 				<li>
-					<a href="service?action=listSubscriptions">View and edit</a> your existing subscriptions.</li>
+					<a href="/alerting/service?action=listSubscriptions">View and edit</a> your existing subscriptions.</li>
 				<li>
 					<a href="about.html">Read more</a> about the alerting service.</li>
+				<li>
+					<a href="/alerting/service?action=logout">Log out</a></li?
 			</ul>
 		</div>
Index: /trunk/gsdl3/packages/gsdl-as/build.xml
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/build.xml	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/build.xml	(revision 8738)
@@ -217,7 +217,7 @@
     <!-- Include all JAR files that will be included in /WEB-INF/lib -->
     <!-- *** CUSTOMIZE HERE AS REQUIRED BY YOUR APPLICATION *** -->
-<!--
-    <pathelement location="${foo.jar}"/>
--->
+    <fileset dir="lib">
+    	<include name="*.jar"/>
+    </fileset>
 
     <!-- Include all elements that Tomcat exposes to applications -->
Index: /trunk/gsdl3/packages/gsdl-as/lib/templates/login.vm
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/lib/templates/login.vm	(revision 8738)
+++ /trunk/gsdl3/packages/gsdl-as/lib/templates/login.vm	(revision 8738)
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Greenstone Digital Library Software</title>
+<link href="/alerting/form.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="content">
+<h3>Alerting Service for Greenstone</h3>
+<p>Login</p>
+#if ($error) 
+<div class="error">
+An error occured:<br/>
+$errormessage
+</div>
+#end
+<form action="/alerting/service?action=login" method="post"/>
+<table>
+<tr>
+<th><label for="username">username</label></th><td><input type="text" name="username" id="username"/></td>
+</tr>
+<tr>
+<th><label for="password">password</label></th><td><input type="password" name="password" id="password"/></td>
+</tr>
+</table>
+<br/>
+<input type="submit" value="login"/>
+</form>
+<div class="description">Don't have an account yet? <a href="/alerting/service?action=showRegistrationForm">Register</a> now!</a></div>
+</div>
+
+</body>
+</html>
Index: /trunk/gsdl3/packages/gsdl-as/lib/templates/register.vm
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/lib/templates/register.vm	(revision 8738)
+++ /trunk/gsdl3/packages/gsdl-as/lib/templates/register.vm	(revision 8738)
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Greenstone Digital Library Software</title>
+<link href="/alerting/form.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+<div id="content">
+<h3>Alerting Service for Greenstone</h3>
+<p>Register</p>
+#if ($error)
+<div class="error">
+An error occured:<br/>
+$errormessage
+</div>
+#end
+<form action="/alerting/service?action=register" method="post"/>
+<table>
+<tr>
+<th><label for="username">username</label></th><td><input type="text" name="username" id="username"/></td>
+</tr>
+<tr>
+<th><label for="password">password</label></th><td><input type="password" name="password" id="password"/></td>
+</tr>
+<tr>
+<th><label for="password2">repeat password</label></th><td><input type="password" name="password2" id="password2"/></td>
+</tr>
+</table>
+<br/>
+<input type="submit" value="create account"/>
+</form>
+</div>
+
+</body>
+</html>
Index: /trunk/gsdl3/packages/gsdl-as/lib/templates/sub_collection.vm
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/lib/templates/sub_collection.vm	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/lib/templates/sub_collection.vm	(revision 8738)
@@ -37,18 +37,13 @@
 					<ol>
 					<li>You can choose one or more collections from the list below. Only events from the collections(s) you have chosen will be sent to you<br/>
-					<select multiple="multiple" name="host" size="7" style="margin:5px 0px;">
-					<optgroup label="localhost">
-					<option>greenstone 2 mg demo</option>
-					<option>greenstone 2 mgpp demo</option>
-					<option>greenstone 3 mg demo</option>
-					<option selected="selected">any collection on host localhost</option>
-					</optgroup>
-					<optgroup label="nzdl.org">
-					<option>acronym extraction demo</option>
-					<option>music library</option>
-					<option>youth oral history</option>
-					<option>kiwi aircraft images</option>
-					<option selected="selected">any collection on host nzdl.org</option>
-					</optgroup>
+					<select multiple="multiple" name="collection" size="7" style="margin:5px 0px;">
+						#foreach($host in $hostnames)
+						<optgroup label="$host">
+							#foreach($collection in $collectionnames.get($host))
+							<option>$collection</option>
+							#end
+							<option selected="selected">any collection on host $host</option>
+						</optgroup>
+					#end
 					</select></li>
 					<li>You can enter a query in the field below. Only events coming from a collection whose name matches this query will be sent to you.<br/>
@@ -59,5 +54,5 @@
 							</td>
 							<td>
-								<input type="text" name="document_title" style="width:80%;"/>
+								<input type="text" name="collection_query" style="width:80%;" value="$!prefill.collection_query"/>
 							</td>
 						</tr>
Index: /trunk/gsdl3/packages/gsdl-as/lib/templates/sub_host.vm
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/lib/templates/sub_host.vm	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/lib/templates/sub_host.vm	(revision 8738)
@@ -38,8 +38,9 @@
 					<li>You can choose one or more hosts from the list below. Only events from the host(s) you have chosen will be sent to you.<br/>
 					<select multiple="multiple" name="host" size="3" style="margin:5px 0px;">
-					<option selected="selected">localhost</option>
-					<option>www.greenstone.org</option>
-					<option>www.nzdl.org</option>
-					<option>any host</option>
+					#set($firstoption = true);
+					#foreach($host in $hostnames)
+					<option #if($firstoption) selected="selected" #set($firstoption = false) #end>$host</option>
+					#end
+					<option value="any">any host</option>
 					</select></li>
 					<li>You can enter a query in the field below. Only events coming from a host whose name matches this query will be sent to you.<br/>
@@ -50,5 +51,5 @@
 							</td>
 							<td>
-								<input type="text" name="document_title" style="width:80%;"/>
+								<input type="text" name="host_query" style="width:80%;" value="$!prefill.host_query"/>
 							</td>
 						</tr>
Index: /trunk/gsdl3/packages/gsdl-as/lib/templates/sub_notification.vm
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/lib/templates/sub_notification.vm	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/lib/templates/sub_notification.vm	(revision 8738)
@@ -37,5 +37,5 @@
 					You will be able to see the subscription in the list of all your subscriptions after logging in to the alerting service. <br/>
 					You can assign a name to your subscription that makes it easier for you to recognise it: 
-					<input name="subscription_name" type="text"/>
+					<input name="subscription_name" type="text" value="$!prefill.subscription_name"/>
 					</div>
 					<br/>
@@ -52,5 +52,5 @@
 							</td>
 							<td>
-								<input type="text" name="email"/>
+								<input type="text" name="email" value="$!prefill.email"/>
 							</td>
 						</tr>
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/AlertingService.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/AlertingService.java	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/AlertingService.java	(revision 8738)
@@ -14,5 +14,4 @@
 import java.util.*;
 
-import javax.mail.Session;
 import javax.servlet.http.*;
 
@@ -20,4 +19,5 @@
 import org.apache.velocity.context.Context;
 import org.apache.velocity.servlet.VelocityServlet;
+import org.greenstone.gsdlas.users.UserManagementException;
 import org.greenstone.gsdlas.users.UserManager;
 
@@ -30,14 +30,19 @@
 public class AlertingService extends VelocityServlet {
     
-    public static final Set actions = new TreeSet();
+    public static final Set actions;
     static {
-        actions.add("createSubscription");
-        actions.add("deleteSubscription");
-        actions.add("editSubscription");
-        actions.add("showFeed");
-        actions.add("listSubscriptions");
-        actions.add("login");
-        actions.add("register");
-        actions.add("logout");
+        Set set = new TreeSet();
+        set.add("createSubscription");
+        set.add("deleteSubscription");
+        set.add("editSubscription");
+        set.add("showFeed");
+        set.add("listSubscriptions");
+        
+        set.add("login");
+        set.add("register");
+        set.add("logout");
+        set.add("showLoginForm");
+        set.add("showRegistrationForm");
+        actions = Collections.unmodifiableSet(set);
     }
     
@@ -67,5 +72,7 @@
         } catch (Exception e) {
             String message = "An error has occured, I couldn't do what you told me to do.";
-            String details = e.getMessage() + " (" + e.getClass().getName() + "), action is " + action;
+            String details = e.getMessage() + " (" + e.getClass().getName() + "); "
+            	+ e.getCause() 
+            	+ " ; action is " + action;
             return showError(context, message, details);
         }
@@ -99,7 +106,8 @@
         if (!UserManager.getInstance().isLoggedIn(session)) {
             session.setAttribute("next_action", "createSubscription");
-            return "login.vm";
+            return showLoginForm(arguments, context);
         }
         if (arguments.containsKey("next_page") && arguments.get("next_page").equals("finish")) {
+            arguments.putAll(getPageArgsFromSession(session));
             ProfileStore.getInstance().createSubscription(arguments);
             return listSubscriptions(arguments, context);
@@ -113,5 +121,5 @@
         if (!UserManager.getInstance().isLoggedIn(session)) {
             session.setAttribute("next_action", "deleteSubscription");
-            return "login.vm";
+            return showLoginForm(arguments, context);
         }
         String subscriptionID = (String) arguments.get("subscriptionID");
@@ -124,5 +132,5 @@
         if (!UserManager.getInstance().isLoggedIn(session)) {
             session.setAttribute("next_action", "editSubscription");
-            return "login.vm";
+            return showLoginForm(arguments, context);
         }
         if (arguments.containsKey("next_page") && arguments.get("next_page").equals("finish")) {
@@ -138,7 +146,21 @@
         return "feed.vm";
     }
+    
+    public String showLoginForm(Map arguments, Context context) {
+        return "login.vm";
+    }
+    
+    public String showRegistrationForm(Map arguments, Context context) {
+        return "register.vm";
+    }
 
     public String listSubscriptions(Map arguments, Context context) {
-        context.put("title", "List of Subscriptions");
+        HttpSession session = ((HttpServletRequest)context.get(REQUEST)).getSession(true);
+        if (!UserManager.getInstance().isLoggedIn(session)) {
+            session.setAttribute("next_action", "listSubscriptions");
+            return showLoginForm(arguments, context);
+        }
+        String username = (String) session.getAttribute("username");
+        context.put("title", "List of Subscriptions for " + username);
         context.put("list", ProfileStore.getInstance().getAllSubscriptions());
         return "list.vm";
@@ -147,5 +169,11 @@
     public String login(Map arguments, Context context) throws Exception {
         HttpSession session = ((HttpServletRequest)context.get(REQUEST)).getSession(true);
-        UserManager.getInstance().loginUser(arguments, session);
+        try {
+            UserManager.getInstance().loginUser(arguments, session);
+        } catch (UserManagementException e) {
+            context.put("error", Boolean.TRUE);
+            context.put("errormessage", e.getMessage());
+            return showLoginForm(arguments, context); 
+        }
         if (session.getAttribute("next_action") != null) {
             String nextAction = (String) session.getAttribute("next_action");
@@ -158,5 +186,11 @@
     public String register(Map arguments, Context context) throws Exception {
         HttpSession session = ((HttpServletRequest)context.get(REQUEST)).getSession(true);
-        UserManager.getInstance().createUser(arguments, session);
+        try {
+            UserManager.getInstance().createUser(arguments, session);
+        } catch (UserManagementException e) {
+            context.put("error", Boolean.TRUE);
+            context.put("errormessage", e.getMessage());
+            return showRegistrationForm(arguments, context);
+        }
         return login(arguments, context);
     }
@@ -194,6 +228,11 @@
         for (Iterator iter = args.keySet().iterator(); iter.hasNext();) {
             String key = (String) iter.next();
-            if (!key.equals("action")) {
-                String firstValue = ((String[]) args.get(key))[0];
+            if (key.equals("type") || key.equals("host") 
+                    || key.equals("collection") || key.equals("way")) {
+                // multi-valued attributes
+                String[] values = ((String[]) args.get(key));
+                result.put(key, Arrays.asList(values));
+            } else {
+                String firstValue = ((String[])args.get(key))[0];
                 result.put(key, firstValue);
             }
@@ -260,7 +299,32 @@
         // get page-specific stuff
         if (nextPage.equals("host")) {
-            
+            GreenstoneCommunicator gsComm = null;
+            String[] hostNames;
+            try {
+                gsComm = new GreenstoneCommunicator();
+                hostNames = gsComm.getHostNames();
+            } catch (Exception e) {
+                hostNames = new String[] { "localhost" };
+            }
+            context.put("hostnames", hostNames);
         } else if (nextPage.equals("collection")) {
-            
+            // TODO might be query instead of just name
+            List hostNames = (List) arguments.get("host");
+            GreenstoneCommunicator gsComm = null;
+            Map collNames = new TreeMap();
+            for (Iterator iter = hostNames.iterator(); iter.hasNext();) {
+                String host = (String) iter.next();
+                Set collNamesForHost = new TreeSet();   
+                try {
+                    gsComm = new GreenstoneCommunicator(new URL("http://" + host + ":8080/soap/servlet/rpcrouter"));
+                    collNamesForHost.addAll(Arrays.asList(gsComm.getCollectionNames()));
+                } catch (Exception e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }	
+                collNames.put(host, collNamesForHost);
+            }
+            context.put("collectionnames", collNames);
+            context.put("hostnames", hostNames);
         }
         
@@ -324,4 +388,16 @@
     }
 
+    private Map getPageArgsFromSession(HttpSession session) {
+        Map result = new TreeMap();
+        Map pageArgs = (Map) session.getAttribute("page_args");
+        if (pageArgs != null) {
+            for (Iterator iter = pageArgs.values().iterator(); iter.hasNext();) {
+                Map args = (Map) iter.next();
+                result.putAll(args);
+            }
+        }
+        return result;
+    }
+    
     /**
      * @param page
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/GreenstoneCommunicator.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/GreenstoneCommunicator.java	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/GreenstoneCommunicator.java	(revision 8738)
@@ -9,5 +9,4 @@
 package org.greenstone.gsdlas;
 
-import java.io.OutputStreamWriter;
 import java.net.URL;
 import java.util.*;
@@ -18,5 +17,4 @@
 import org.apache.soap.Fault;
 import org.apache.soap.rpc.*;
-import org.apache.soap.util.xml.DOM2Writer;
 import org.w3c.dom.*;
 
@@ -44,4 +42,16 @@
     }
 
+    /**
+     * 
+     */
+    public GreenstoneCommunicator() {
+        
+        // TODO Auto-generated constructor stub
+    }
+
+    public String[] getHostNames() {
+        return new String[] {"localhost"};
+    }
+    
     public String[] getCollectionNames() throws Exception {
         Document document = createSubsetDescriptionRequest("", "collectionList");
@@ -56,4 +66,6 @@
             result[i] = name.getNodeValue();
         }
+        
+        System.out.println(Arrays.toString(result));
         
         return result;
@@ -139,5 +151,5 @@
     private Element sendToGreenstone(Element message) throws Exception {
         Call call = new Call();
-        call.setTargetObjectURI("teevee.localsite");
+        call.setTargetObjectURI("localsite");
         call.setMethodName("process");
         // set Encoding Style to use literal XML
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/NotificationStore.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/NotificationStore.java	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/NotificationStore.java	(revision 8738)
@@ -9,9 +9,5 @@
 package org.greenstone.gsdlas;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
 import java.util.*;
-import java.util.Map;
-import java.util.Set;
 
 import org.greenstone.gsdlas.profiles.Subscription;
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/ProfileStore.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/ProfileStore.java	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/ProfileStore.java	(revision 8738)
@@ -14,6 +14,4 @@
 
 import org.greenstone.gsdlas.profiles.*;
-import org.greenstone.gsdlas.profiles.IdEqualsPredicate;
-import org.greenstone.gsdlas.profiles.Subscription;
 
 /**
@@ -33,5 +31,4 @@
 
     private ProfileStore() {
-
     }
 
@@ -206,5 +203,5 @@
         for (Iterator iter = matchedPredicates.iterator(); iter.hasNext();) {
             Predicate pred = (Predicate) iter.next();
-            for (Iterator iterator = pred.getSubscriptions().iterator(); iterator
+            for (Iterator iterator = pred.getSubscriptionIDs().iterator(); iterator
                     .hasNext();) {
                 Subscription sub = (Subscription) iterator.next();
@@ -273,16 +270,8 @@
     /**
      * @param arguments
-     * @throws ParseException
-     */
-    public void createSubscription(Map arguments) throws ParseException {
-        // TODO pass user information to profile store
+     * @throws Exception
+     */
+    public void createSubscription(Map arguments) throws Exception {
         Subscription sub = new Subscription(arguments);
-        // TODO this should probably be somewhere else
-        for (Iterator iter = sub.getPredicates().iterator(); iter.hasNext();) {
-            Predicate predicate = (Predicate) iter.next();
-            if (predicate != null) {
-                predicate.addSubscription(sub);
-            }
-        }
         addSubscription(sub);
     }
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseException.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseException.java	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseException.java	(revision 8738)
@@ -20,3 +20,10 @@
         super(message, cause);
     }
+
+    /**
+     * @param string
+     */
+    public DatabaseException(String message) {
+        super(message);
+    }
 }
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseManager.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseManager.java	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/database/DatabaseManager.java	(revision 8738)
@@ -37,8 +37,8 @@
     public Connection getDatabaseConnection() throws DatabaseException {
         if(database == null) {
-            String connectionString = "jdbc:mysql://localhost/alerting";
+            String connectionString = "jdbc:mysql://localhost/alerting?user=gsdl3admin";
             try {
                 Class.forName("com.mysql.jdbc.Driver").newInstance();
-                Connection connection = DriverManager.getConnection(connectionString);
+                database = DriverManager.getConnection(connectionString);
             } catch (Exception e) {
                 throw new DatabaseException("Could not establish connection to database (" + connectionString + ")", e);
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/profiles/Predicate.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/profiles/Predicate.java	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/profiles/Predicate.java	(revision 8738)
@@ -9,7 +9,14 @@
 package org.greenstone.gsdlas.profiles;
 
+import java.sql.*;
+import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.*;
 import java.util.Set;
 import java.util.TreeSet;
+
+import org.greenstone.gsdlas.database.DatabaseException;
+import org.greenstone.gsdlas.database.DatabaseManager;
+import org.greenstone.gsdlas.util.ArrayHelper;
 
 /**
@@ -26,6 +33,19 @@
     protected int id;
     
-    public void addSubscription(Subscription sub) {
-        subscriptions.add(sub);
+
+    public static final String[] singleValueFields = new String[] {
+            "document_title",
+            "document_content",
+            "metadata_has_field",
+            "host_query",
+            "collection_query" };
+    public static final String[] multiValueFields = new String[] {
+            "type",
+            "host",
+            "collection" };
+    
+    public void addSubscription(int subscriptionID) {
+        subscriptions.add(new Integer(subscriptionID));
+        // TODO save to db
     }
 
@@ -33,5 +53,5 @@
      * @see org.greenstone.gsdlas.profiles.Predicate#getSubscriptions()
      */
-    public Set getSubscriptions() {
+    public Set getSubscriptionIDs() {
         return Collections.unmodifiableSet(subscriptions);
     }
@@ -74,4 +94,50 @@
     
     public abstract boolean isSatisfied(Map event);
+
+    /**
+     * @param key
+     * @return
+     */
+    public static boolean isSingleValued(String key) {
+        return ArrayHelper.contains(singleValueFields, key);
+    }
+
+    /**
+     * @param key
+     * @return
+     */
+    public static boolean isMultiValued(String key) {
+        return ArrayHelper.contains(singleValueFields, key);
+    }
+
+    /**
+     * @return
+     * @throws DatabaseException
+     * @throws SQLException
+     */
+    int saveToDatabase() throws SQLException, DatabaseException {
+        // TODO handle multi-valued predicate (in subclass?)
+        String query = "SELECT id FROM predicates " +
+        		"WHERE type='single' AND field = '" + field + 
+        		"' AND singleValue = '" + value + "';";
+        Statement statement = DatabaseManager.getInstance().getDatabaseConnection().createStatement();
+        System.out.println(query);
+        ResultSet result = statement.executeQuery(query);
+        if (result.next()) { // predicate already exists in database
+            System.out.println("predicate " + id + " already exists");
+            return result.getInt("id");
+        }
+        // predicate has been newly created
+        String insert = "INSERT INTO predicates (type, field, singleValue) " +
+        "VALUES ('single', '" + field + "', '" + value + "');";
+        System.out.println(insert);
+        statement.execute(insert);
+        result = statement.executeQuery(query);
+        if (result.next()) {
+            return result.getInt("id");
+        } else {
+            throw new DatabaseException("could not save predicate");
+        }
+    }
     
 }
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/profiles/PredicateFactory.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/profiles/PredicateFactory.java	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/profiles/PredicateFactory.java	(revision 8738)
@@ -9,7 +9,10 @@
 package org.greenstone.gsdlas.profiles;
 
+import java.sql.SQLException;
 import java.util.*;
 import java.util.Map;
 import java.util.TreeMap;
+
+import org.greenstone.gsdlas.database.DatabaseException;
 
 
@@ -42,7 +45,13 @@
      * @return
      * @throws ParseException
+     * @throws DatabaseException
+     * @throws SQLException
      */
     public static Predicate createPredicate(String key, String value)
-            throws ParseException {
+            throws SQLException, DatabaseException {
+        System.out.println("creating predicate for key " + key + " and value " + value);
+        if (!Predicate.isSingleValued(key)) {
+            return null;
+        }
         Predicate result = null;
         if (value == null || value.length() == 0) {
@@ -59,9 +68,16 @@
             result = createSubstringMatchPredicate(key, value);
         } else {
-            throw new ParseException("value " + value + " for field " + key
-                    + " does not lead to a parseable predicate");
+        	return null;
         }
-        result.setID(id++);
+        result.setID(result.saveToDatabase());
         return result;
+    }
+    
+    public static Predicate createPredicate(String key, List values) throws SQLException, DatabaseException {
+        if (!Predicate.isMultiValued(key)) {
+            return null;
+        }
+        // TODO properly handle multi-valued predicates
+        return createPredicate(key, (String) values.get(0));
     }
 
@@ -108,5 +124,5 @@
         return predicate;
     }
-
+    
     /**
      * @param field
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/profiles/Subscription.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/profiles/Subscription.java	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/profiles/Subscription.java	(revision 8738)
@@ -9,7 +9,13 @@
 package org.greenstone.gsdlas.profiles;
 
+import java.sql.*;
+import java.sql.Connection;
+import java.sql.Statement;
 import java.util.*;
 import java.util.Map;
 import java.util.TreeMap;
+
+import org.greenstone.gsdlas.database.DatabaseException;
+import org.greenstone.gsdlas.database.DatabaseManager;
 
 /**
@@ -21,16 +27,29 @@
 public class Subscription implements Comparable {
     private Map map;
+    
     private int id;
+    private String username;
+    private String name;
+    private String email;
+    private boolean rssNotification;
+    private boolean eventsSincePageNotification;
+    
     private int numOfNonEqualsPredicates;
     
-    private static int highestId = 0;
-
-    public Subscription(Map valueMap) throws ParseException {
+    public Subscription(Map valueMap) throws DatabaseException, SQLException {
         map = new TreeMap();
         for (Iterator iter = valueMap.keySet().iterator(); iter.hasNext();) {
             String key = (String) iter.next();
-            String value = (String) valueMap.get(key);
-            Predicate predicate = PredicateFactory.createPredicate(key, value); 
-            map.put(key, predicate);
+            Object value = valueMap.get(key);
+            
+            Predicate predicate = null;
+            if (value instanceof String) {
+                predicate = PredicateFactory.createPredicate(key, (String) value);  
+                map.put(key, predicate);
+            } else if (value instanceof List) {
+                List values = (List)value;
+                predicate = PredicateFactory.createPredicate(key, values);  
+                map.put(key, predicate);
+            } 
             
             if (predicate != null && !(predicate instanceof IdEqualsPredicate)) {
@@ -38,5 +57,20 @@
             }
         }
-        id = highestId++;
+        
+        System.out.println("finished creating predicates");
+        
+        username = (String) valueMap.get("username");
+        name = (String) valueMap.get("subscription_name");
+        email = (String) valueMap.get("email");
+        rssNotification = valueMap.containsKey("way") && ((List)valueMap.get("way")).contains("rss");
+        eventsSincePageNotification = valueMap.containsKey("way") && ((List)valueMap.get("way")).contains("page");
+        id = saveToDatabase(true);
+        
+        for (Iterator iter = getPredicates().iterator(); iter.hasNext();) {
+            Predicate predicate = (Predicate) iter.next();
+            if (predicate != null) {
+                predicate.addSubscription(id);
+            }
+        }
     }
     
@@ -95,3 +129,44 @@
         return new Integer(id).compareTo(new Integer(other.id));
     }
+    
+    private int saveToDatabase(boolean initial) throws DatabaseException, SQLException {
+        Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
+        Statement statement = conn.createStatement();
+        String sqlString;
+        if (initial) {
+            sqlString = "INSERT INTO subscriptions (name, email, rss, page, user) " +
+            		"VALUES ('" + name + "','" + email + "'," + (rssNotification ? 1 : 0) +
+            		"," + (eventsSincePageNotification ? 1: 0)+ ", '" + username + "');";
+        } else {
+            sqlString = "UPDATE subscriptions SET name='" + name + "', email='" +
+            		email + "', rss=" + (rssNotification ? 1 : 0) + ",page=" + 
+            		(eventsSincePageNotification ? 1 : 0)+ "WHERE id=" + id + ";";
+            // cannot change user
+        }
+        System.out.println(sqlString);
+        statement.executeUpdate(sqlString);
+        
+        sqlString = "SELECT id FROM subscriptions WHERE name like '" + name + 
+        			"' AND email like '" + email + "' AND rss=" + (rssNotification ? 1 : 0) 
+        			+ " AND page=" + (eventsSincePageNotification ? 1 : 0) + " AND user like '" +
+        			username + "';";
+        System.out.println(sqlString);
+        ResultSet result = statement.executeQuery(sqlString);
+        int id;
+        if (result.next()) {
+            id = result.getInt("id");
+        } else {
+            throw new DatabaseException("Couldn't save subscription");
+        }
+        
+        if (initial) {
+           for (Iterator iter = getPredicates().iterator(); iter.hasNext();) {
+               Predicate predicate = (Predicate) iter.next();
+               sqlString = "INSERT INTO subs_to_predicates (subscription, predicate) " +
+               		"VALUES (" + id + "," + predicate.getID() + ");";
+               statement.executeUpdate(sqlString);
+           }
+        }
+        return id;
+    }
 }
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/users/PasswordMismatchException.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/users/PasswordMismatchException.java	(revision 8738)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/users/PasswordMismatchException.java	(revision 8738)
@@ -0,0 +1,19 @@
+/*
+ * Created on Dec 3, 2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.greenstone.gsdlas.users;
+
+/**
+ * @author andrea
+ *
+ * TODO To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+public class PasswordMismatchException extends Exception {
+    public PasswordMismatchException(String message) {
+        super(message);
+    }
+}
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/users/UserManagementException.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/users/UserManagementException.java	(revision 8738)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/users/UserManagementException.java	(revision 8738)
@@ -0,0 +1,29 @@
+/*
+ * Created on Dec 3, 2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.greenstone.gsdlas.users;
+
+
+/**
+ * @author andrea
+ *
+ * TODO To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+public class UserManagementException extends Exception {
+
+    /**
+     * @param e
+     */
+    public UserManagementException(String message, Throwable cause) {
+        super(message, cause);
+    }
+    
+    public UserManagementException(String message) {
+        super(message);
+    }
+
+}
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/users/UserManager.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/users/UserManager.java	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/users/UserManager.java	(revision 8738)
@@ -9,7 +9,10 @@
 package org.greenstone.gsdlas.users;
 
+import java.sql.*;
 import java.util.Map;
 
 import javax.servlet.http.HttpSession;
+
+import org.greenstone.gsdlas.database.DatabaseManager;
 
 /**
@@ -40,7 +43,5 @@
         if (session.getCreationTime() - session.getLastAccessedTime() > session.getMaxInactiveInterval())
             return false;
-        
-        // TODO Auto-generated method stub
-        return true;
+        return session.getAttribute("username") != null;
     }
 
@@ -48,7 +49,32 @@
      * @param arguments
      * @param session
+     * @throws PasswordMismatchException
+     * @throws UserManagementException
      */
-    public void createUser(Map arguments, HttpSession session) {
-        // TODO Auto-generated method stub
+    public void createUser(Map arguments, HttpSession session) throws PasswordMismatchException, UserManagementException {
+        if (!arguments.get("password").equals(arguments.get("password2"))) {
+            throw new PasswordMismatchException("The passwords don't match");
+        }
+        String username = (String) arguments.get("username");
+        
+        byte[] password = ((String) arguments.get("password")).getBytes();
+        
+        byte[] pwdHash = password;
+//        try {
+//            pwdHash = MessageDigest.getInstance("MD5").digest(password);
+//        } catch (NoSuchAlgorithmException e) {
+//            e.printStackTrace();
+//            throw new UserManagementException("could not create user", e);
+//        }
+        
+        try {
+            Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
+            Statement statement = conn.createStatement();
+            statement.executeUpdate("INSERT INTO users (username, password) " +
+            		"VALUES ('" + username + "','" + new String(pwdHash) + "')");
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new UserManagementException("could not create user", e);
+        }
         
     }
@@ -57,8 +83,37 @@
      * @param arguments
      * @param session
+     * @throws UserManagementException
      */
-    public void loginUser(Map arguments, HttpSession session) {
-        // TODO Auto-generated method stub
+    public void loginUser(Map arguments, HttpSession session) throws UserManagementException {
+        String username = (String) arguments.get("username");
         
+        if (isLoggedIn(session) && session.getAttribute("username").equals(username)) {
+            return; // already logged in
+        }
+        
+        byte[] password = ((String) arguments.get("password")).getBytes();
+        
+        byte[] pwdHash = password;
+        try {
+//            pwdHash = MessageDigest.getInstance("MD5").digest(password);
+            
+            Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
+            Statement statement = conn.createStatement();
+            ResultSet results = statement.executeQuery("SELECT password " +
+            		"FROM users WHERE username like '" + username + "';");
+            String pwdFromDB = "";
+            if(results.next()) {
+                pwdFromDB = results.getString("password");
+            }
+            if (!pwdFromDB.equals(new String(pwdHash))) {
+                throw new PasswordMismatchException("user " + username 
+                        + " is unknown, or the passwords don't match");
+            }
+            session.setAttribute("username", username);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new UserManagementException("could not login user", e);
+        }
+
     }
     
Index: /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/util/ArrayHelper.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/util/ArrayHelper.java	(revision 8738)
+++ /trunk/gsdl3/packages/gsdl-as/src/org/greenstone/gsdlas/util/ArrayHelper.java	(revision 8738)
@@ -0,0 +1,26 @@
+/*
+ * Created on Dec 5, 2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package org.greenstone.gsdlas.util;
+
+import java.util.Arrays;
+
+/**
+ * @author andrea
+ *
+ * TODO To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+public class ArrayHelper {
+    static public boolean contains(String[] array, String element) {
+        if (array == null || element == null) {
+            return false;
+        }
+        Arrays.sort(array);
+        int index = Arrays.binarySearch(array, element);
+        return index > -1 && index <= array.length;
+    }
+}
Index: /trunk/gsdl3/packages/gsdl-as/test-src/org/greenstone/gsdlas/ProfileStoreTest.java
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/test-src/org/greenstone/gsdlas/ProfileStoreTest.java	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/test-src/org/greenstone/gsdlas/ProfileStoreTest.java	(revision 8738)
@@ -13,6 +13,4 @@
 
 import junit.framework.TestCase;
-
-import org.greenstone.gsdlas.profiles.*;
 
 /**
@@ -31,5 +29,5 @@
         try {
             addSubscriptions();
-        } catch (ParseException e) {
+        } catch (Exception e) {
             e.printStackTrace();
             fail(e.getMessage());
@@ -60,5 +58,5 @@
     }
 
-    private void addSubscriptions() throws ParseException {
+    private void addSubscriptions() throws Exception {
         Map valueMap = new TreeMap();
         
@@ -83,5 +81,5 @@
     }
     
-    private void addSubscription(Map valueMap) throws ParseException {  
+    private void addSubscription(Map valueMap) throws Exception {  
         ProfileStore.getInstance().createSubscription(valueMap);    
     }
Index: /trunk/gsdl3/packages/gsdl-as/web/form.css
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/web/form.css	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/web/form.css	(revision 8738)
@@ -40,4 +40,5 @@
 	color: #555;
 	border-top: 10px solid #CCC;
+	font-family: "Trebuchet MS", Arial, helvetica, sans-serif;
 }
 
@@ -49,5 +50,4 @@
 #progress td.current {
 	background-color: #F0E68C;
-	font-style: italic;
 	color: #000;
 }
Index: /trunk/gsdl3/packages/gsdl-as/web/index.html
===================================================================
--- /trunk/gsdl3/packages/gsdl-as/web/index.html	(revision 8737)
+++ /trunk/gsdl3/packages/gsdl-as/web/index.html	(revision 8738)
@@ -17,9 +17,14 @@
 			<ul>
 				<li>
+					<a href="/alerting/service?action=showLoginForm">Log in</a></li>
+				</li>
+				<li>
 					<a href="subscribe.html">Create</a> a subscription.</li>
 				<li>
-					<a href="service?action=listSubscriptions">View and edit</a> your existing subscriptions.</li>
+					<a href="/alerting/service?action=listSubscriptions">View and edit</a> your existing subscriptions.</li>
 				<li>
 					<a href="about.html">Read more</a> about the alerting service.</li>
+				<li>
+					<a href="/alerting/service?action=logout">Log out</a></li?
 			</ul>
 		</div>
