You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by mr...@apache.org on 2006/04/07 00:31:41 UTC

svn commit: r392100 [2/4] - in /struts/action/trunk/apps: faces-example1/src/main/java/ faces-example1/src/main/java/org/ faces-example1/src/main/java/org/apache/ faces-example1/src/main/java/org/apache/struts/ faces-example1/src/main/java/org/apache/s...

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/LogonForm.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/LogonForm.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/LogonForm.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/LogonForm.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,146 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for the user profile page.  This form has the following fields,
+ * with default values in square brackets:
+ * <ul>
+ * <li><b>password</b> - Entered password value
+ * <li><b>username</b> - Entered username value
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $
+ */
+
+public final class LogonForm extends ActionForm {
+
+
+    // --------------------------------------------------- Instance Variables
+
+
+    /**
+     * The password.
+     */
+    private String password = null;
+
+
+    /**
+     * The username.
+     */
+    private String username = null;
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    /**
+     * Return the password.
+     */
+    public String getPassword() {
+
+	return (this.password);
+
+    }
+
+
+    /**
+     * Set the password.
+     *
+     * @param password The new password
+     */
+    public void setPassword(String password) {
+
+        this.password = password;
+
+    }
+
+
+    /**
+     * Return the username.
+     */
+    public String getUsername() {
+
+	return (this.username);
+
+    }
+
+
+    /**
+     * Set the username.
+     *
+     * @param username The new username
+     */
+    public void setUsername(String username) {
+
+        this.username = username;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        this.password = null;
+        this.username = null;
+
+    }
+
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public ActionErrors validate(ActionMapping mapping,
+                                 HttpServletRequest request) {
+
+        ActionErrors errors = new ActionErrors();
+        if ((username == null) || (username.length() < 1))
+            errors.add("username", new ActionError("error.username.required"));
+        if ((password == null) || (password.length() < 1))
+            errors.add("password", new ActionError("error.password.required"));
+
+        return errors;
+
+    }
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/MainMenuBacking.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/MainMenuBacking.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/MainMenuBacking.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/MainMenuBacking.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example;
+
+
+import javax.faces.context.FacesContext;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * <p>Backing bean for the <code>mainMenu.jsp</code> page.</p>
+ */
+
+public class MainMenuBacking extends AbstractBacking {
+
+
+    // -------------------------------------------------------- Static Variables
+
+
+    private static final Log log = LogFactory.getLog(MainMenuBacking.class);
+
+
+    // ----------------------------------------------------------------- Actions
+
+
+    /**
+     * <p>Forward to the <em>Edit Registration</em> action.</p>
+     */
+    public String edit() {
+
+        if (log.isDebugEnabled()) {
+            log.debug("edit()");
+        }
+        FacesContext context = FacesContext.getCurrentInstance();
+        StringBuffer sb = registration(context);
+        sb.append("?action=Edit");
+        forward(context, sb.toString());
+        return (null);
+
+    }
+
+
+    /**
+     * <p>Forward to the <em>Logoff</em> action.</p>
+     */
+    public String logoff() {
+
+        if (log.isDebugEnabled()) {
+            log.debug("logoff()");
+        }
+        FacesContext context = FacesContext.getCurrentInstance();
+        forward(context, logoff(context).toString());
+        return (null);
+
+    }
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/RegistrationBacking.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/RegistrationBacking.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/RegistrationBacking.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/RegistrationBacking.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,175 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example;
+
+
+import javax.faces.component.UIData;
+import javax.faces.context.FacesContext;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * <p>Backing bean for the <code>registration.jsp</code> page.</p>
+ */
+
+public class RegistrationBacking extends AbstractBacking {
+
+
+    // -------------------------------------------------------- Static Variables
+
+
+    private static final Log log = LogFactory.getLog(RegistrationBacking.class);
+
+
+    // -------------------------------------------------------------- Properties
+
+
+    private UIData table = null;
+
+
+    /**
+     * <p>Return the <code>UIData</code> instance we are bound to.</p>
+     */
+    public UIData getTable() {
+
+        return (this.table);
+
+    }
+
+
+    /**
+     * <p>Set the <code>UIData</code> instance we are bound to.</p>
+     *
+     * @param table The <code>UIData</code> instance
+     */
+    public void setTable(UIData table) {
+
+        this.table = table;
+
+    }
+
+
+
+    // ----------------------------------------------------------------- Actions
+
+
+    /**
+     * <p>Create a new subscription.</p>
+     */
+    public String create() {
+
+        if (log.isDebugEnabled()) {
+            log.debug("create()");
+        }
+        FacesContext context = FacesContext.getCurrentInstance();
+        StringBuffer url = subscription(context);
+        url.append("?action=Create");
+        url.append("&username=");
+        User user = (User)
+            context.getExternalContext().getSessionMap().get("user");
+        url.append(user.getUsername());
+        forward(context, url.toString());
+        return (null);
+
+    }
+
+
+    /**
+     * <p>Delete an existing subscription.</p>
+     */
+    public String delete() {
+
+        if (log.isDebugEnabled()) {
+            log.debug("delete()");
+        }
+        FacesContext context = FacesContext.getCurrentInstance();
+        StringBuffer url = subscription(context);
+        url.append("?action=Delete");
+        url.append("&username=");
+        User user = (User)
+            context.getExternalContext().getSessionMap().get("user");
+        url.append(user.getUsername());
+        url.append("&host=");
+        Subscription subscription = (Subscription)
+            context.getExternalContext().getRequestMap().get("subscription");
+        url.append(subscription.getHost());
+        forward(context, url.toString());
+        return (null);
+
+    }
+
+
+    /**
+     * <p>Edit an existing subscription.</p>
+     */
+    public String edit() {
+
+        if (log.isDebugEnabled()) {
+            log.debug("edit()");
+        }
+        FacesContext context = FacesContext.getCurrentInstance();
+        StringBuffer url = subscription(context);
+        url.append("?action=Edit");
+        url.append("&username=");
+        User user = (User)
+            context.getExternalContext().getSessionMap().get("user");
+        url.append(user.getUsername());
+        url.append("&host=");
+        Subscription subscription = (Subscription)
+            context.getExternalContext().getRequestMap().get("subscription");
+        url.append(subscription.getHost());
+        forward(context, url.toString());
+        return (null);
+
+    }
+
+
+    /**
+     * <p>Update the subscriptions to reflect any revisions to the
+     * <code>type</code> and <code>autoConnect</code> properties.</p>
+     */
+    public String update() {
+
+        if (log.isDebugEnabled()) {
+            log.debug("update()");
+        }
+
+        FacesContext context = FacesContext.getCurrentInstance();
+
+        // Updates went directly to the underlying rows, so all we need to do
+        // is save the database
+        try {
+            UserDatabase database = (UserDatabase)
+                context.getExternalContext().getApplicationMap().
+                get(Constants.DATABASE_KEY);
+            database.save();
+        } catch (Exception e) {
+            log.error("Database save", e);
+        }
+
+        // Forward back to the edit registration page
+        StringBuffer sb = registration(context);
+        sb.append("?action=Edit");
+        forward(context, sb.toString());
+        return (null);
+
+    }
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/RegistrationForm.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/RegistrationForm.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/RegistrationForm.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/RegistrationForm.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,309 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example;
+
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.validator.ValidatorForm;
+
+
+/**
+ * Form bean for the user registration page.  This form has the following
+ * fields, with default values in square brackets:
+ * <ul>
+ * <li><b>action</b> - The maintenance action we are performing (Create,
+ *     Delete, or Edit).
+ * <li><b>fromAddress</b> - The EMAIL address of the sender, to be included
+ *     on sent messages.  [REQUIRED]
+ * <li><b>fullName</b> - The full name of the sender, to be included on
+ *     sent messages.  [REQUIRED]
+ * <li><b>password</b> - The password used by this user to log on.
+ * <li><b>password2</b> - The confirmation password, which must match
+ *     the password when changing or setting.
+ * <li><b>replyToAddress</b> - The "Reply-To" address to be included on
+ *     sent messages.  [Same as from address]
+ * <li><b>username</b> - The registered username, which must be unique.
+ *     [REQUIRED]
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 54936 $ $Date: 2004-10-16 10:57:09 -0700 (Sat, 16 Oct 2004) $
+ */
+
+public final class RegistrationForm extends ValidatorForm  {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The maintenance action we are performing (Create or Edit).
+     */
+    private String action = "Create";
+
+
+    /**
+     * The from address.
+     */
+    private String fromAddress = null;
+
+
+    /**
+     * The full name.
+     */
+    private String fullName = null;
+
+
+    /**
+     * The password.
+     */
+    private String password = null;
+
+
+    /**
+     * The confirmation password.
+     */
+    private String password2 = null;
+
+
+    /**
+     * The reply to address.
+     */
+    private String replyToAddress = null;
+
+
+
+    /**
+     * The username.
+     */
+    private String username = null;
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    /**
+     * Return the maintenance action.
+     */
+    public String getAction() {
+
+	return (this.action);
+
+    }
+
+
+    /**
+     * Set the maintenance action.
+     *
+     * @param action The new maintenance action.
+     */
+    public void setAction(String action) {
+
+        this.action = action;
+
+    }
+
+
+    /**
+     * Return the from address.
+     */
+    public String getFromAddress() {
+
+	return (this.fromAddress);
+
+    }
+
+
+    /**
+     * Set the from address.
+     *
+     * @param fromAddress The new from address
+     */
+    public void setFromAddress(String fromAddress) {
+
+        this.fromAddress = fromAddress;
+
+    }
+
+
+    /**
+     * Return the full name.
+     */
+    public String getFullName() {
+
+	return (this.fullName);
+
+    }
+
+
+    /**
+     * Set the full name.
+     *
+     * @param fullName The new full name
+     */
+    public void setFullName(String fullName) {
+
+        this.fullName = fullName;
+
+    }
+
+
+    /**
+     * Return the password.
+     */
+    public String getPassword() {
+
+	return (this.password);
+
+    }
+
+
+    /**
+     * Set the password.
+     *
+     * @param password The new password
+     */
+    public void setPassword(String password) {
+
+        this.password = password;
+
+    }
+
+
+    /**
+     * Return the confirmation password.
+     */
+    public String getPassword2() {
+
+	return (this.password2);
+
+    }
+
+
+    /**
+     * Set the confirmation password.
+     *
+     * @param password2 The new confirmation password
+     */
+    public void setPassword2(String password2) {
+
+        this.password2 = password2;
+
+    }
+
+
+    /**
+     * Return the reply to address.
+     */
+    public String getReplyToAddress() {
+
+	return (this.replyToAddress);
+
+    }
+
+
+    /**
+     * Set the reply to address.
+     *
+     * @param replyToAddress The new reply to address
+     */
+    public void setReplyToAddress(String replyToAddress) {
+
+        this.replyToAddress = replyToAddress;
+
+    }
+
+
+    /**
+     * Return the username.
+     */
+    public String getUsername() {
+
+	return (this.username);
+
+    }
+
+
+    /**
+     * Set the username.
+     *
+     * @param username The new username
+     */
+    public void setUsername(String username) {
+
+        this.username = username;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        this.action = "Create";
+        this.fromAddress = null;
+        this.fullName = null;
+        this.password = null;
+        this.password2 = null;
+        this.replyToAddress = null;
+        this.username = null;
+
+    }
+
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public ActionErrors validate(ActionMapping mapping,
+                                 HttpServletRequest request) {
+
+        // Perform validator framework validations
+        ActionErrors errors = super.validate(mapping, request);
+
+        // Only need crossfield validations here
+        if (((password == null) && (password2 != null)) ||
+            ((password != null) && (password2 == null)) ||
+            ((password != null) && (password2 != null) &&
+             !password.equals(password2))) {
+            errors.add("password2",
+                       new ActionError("error.password.match"));
+        }
+        return errors;
+
+    }
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SaveRegistrationAction.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SaveRegistrationAction.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SaveRegistrationAction.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SaveRegistrationAction.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,219 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Locale;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Implementation of <strong>Action</strong> that validates and creates or
+ * updates the user registration information entered by the user.  If a new
+ * registration is created, the user is also implicitly logged on.
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 155884 $ $Date: 2005-03-01 22:03:56 -0800 (Tue, 01 Mar 2005) $
+ */
+
+public final class SaveRegistrationAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The <code>Log</code> instance for this application.
+     */
+    private Log log =
+        LogFactory.getLog("org.apache.struts.webapp.Example");
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param form The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception Exception if the application business logic throws
+     *  an exception
+     */
+    public ActionForward execute(ActionMapping mapping,
+				 ActionForm form,
+				 HttpServletRequest request,
+				 HttpServletResponse response)
+	throws Exception {
+
+	// Extract attributes and parameters we will need
+	Locale locale = getLocale(request);
+	HttpSession session = request.getSession();
+	RegistrationForm regform = (RegistrationForm) form;
+	String action = regform.getAction();
+	if (action == null) {
+	    action = "Create";
+        }
+        UserDatabase database = (UserDatabase)
+	  servlet.getServletContext().getAttribute(Constants.DATABASE_KEY);
+        if (log.isDebugEnabled()) {
+            log.debug("SaveRegistrationAction:  Processing " + action +
+                      " action");
+        }
+
+	// Is there a currently logged on user (unless creating)?
+	User user = (User) session.getAttribute(Constants.USER_KEY);
+	if (!"Create".equals(action) && (user == null)) {
+            if (log.isTraceEnabled()) {
+                log.trace(" User is not logged on in session "
+                          + session.getId());
+            }
+	    return (mapping.findForward("logon"));
+        }
+
+	// Was this transaction cancelled?
+	if (isCancelled(request)) {
+            if (log.isTraceEnabled()) {
+                log.trace(" Transaction '" + action +
+                          "' was cancelled");
+            }
+	    session.removeAttribute(Constants.SUBSCRIPTION_KEY);
+	    return (mapping.findForward("failure"));
+	}
+
+        // Validate the transactional control token
+	ActionErrors errors = new ActionErrors();
+        if (log.isTraceEnabled()) {
+            log.trace(" Checking transactional control token");
+        }
+        if (!isTokenValid(request)) {
+            errors.add(ActionErrors.GLOBAL_ERROR,
+                       new ActionError("error.transaction.token"));
+        }
+        resetToken(request);
+
+	// Validate the request parameters specified by the user
+        if (log.isTraceEnabled()) {
+            log.trace(" Performing extra validations");
+        }
+	String value = null;
+	value = regform.getUsername();
+	if (("Create".equals(action)) &&
+            (database.findUser(value) != null)) {
+            errors.add("username",
+                       new ActionError("error.username.unique",
+                                       regform.getUsername()));
+        }
+	if ("Create".equals(action)) {
+	    value = regform.getPassword();
+	    if ((value == null) || (value.length() <1)) {
+                errors.add("password",
+                           new ActionError("error.password.required"));
+            }
+	    value = regform.getPassword2();
+	    if ((value == null) || (value.length() < 1)) {
+                errors.add("password2",
+                           new ActionError("error.password2.required"));
+            }
+	}
+
+	// Report any errors we have discovered back to the original form
+	if (!errors.isEmpty()) {
+	    saveErrors(request, errors);
+            saveToken(request);
+            return (mapping.getInputForward());
+	}
+
+	// Update the user's persistent profile information
+        try {
+            if ("Create".equals(action)) {
+                user = database.createUser(regform.getUsername());
+            }
+            String oldPassword = user.getPassword();
+            PropertyUtils.copyProperties(user, regform);
+            if ((regform.getPassword() == null) ||
+                (regform.getPassword().length() < 1)) {
+                user.setPassword(oldPassword);
+            }
+        } catch (InvocationTargetException e) {
+            Throwable t = e.getTargetException();
+            if (t == null) {
+                t = e;
+            }
+            log.error("Registration.populate", t);
+            throw new ServletException("Registration.populate", t);
+        } catch (Throwable t) {
+            log.error("Registration.populate", t);
+            throw new ServletException("Subscription.populate", t);
+        }
+
+        try {
+            database.save();
+        } catch (Exception e) {
+            log.error("Database save", e);
+        }
+
+        // Log the user in if appropriate
+	if ("Create".equals(action)) {
+	    session.setAttribute(Constants.USER_KEY, user);
+            if (log.isTraceEnabled()) {
+                log.trace(" User '" + user.getUsername() +
+                          "' logged on in session " + session.getId());
+            }
+	}
+
+	// Remove the obsolete form bean
+	if (mapping.getAttribute() != null) {
+            if ("request".equals(mapping.getScope()))
+                request.removeAttribute(mapping.getAttribute());
+            else
+                session.removeAttribute(mapping.getAttribute());
+        }
+
+	// Forward control to the specified success URI
+        if (log.isTraceEnabled()) {
+            log.trace(" Forwarding to success page");
+        }
+	return (mapping.findForward("success"));
+
+    }
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SaveSubscriptionAction.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SaveSubscriptionAction.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SaveSubscriptionAction.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SaveSubscriptionAction.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,205 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Locale;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.action.Action;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.MessageResources;
+
+
+/**
+ * Implementation of <strong>Action</strong> that validates and creates or
+ * updates the mail subscription entered by the user.
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 54936 $ $Date: 2004-10-16 10:57:09 -0700 (Sat, 16 Oct 2004) $
+ */
+
+public final class SaveSubscriptionAction extends Action {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The <code>Log</code> instance for this application.
+     */
+    private Log log =
+        LogFactory.getLog("org.apache.struts.webapp.Example");
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the specified HTTP request, and create the corresponding HTTP
+     * response (or forward to another web component that will create it).
+     * Return an <code>ActionForward</code> instance describing where and how
+     * control should be forwarded, or <code>null</code> if the response has
+     * already been completed.
+     *
+     * @param mapping The ActionMapping used to select this instance
+     * @param form The optional ActionForm bean for this request (if any)
+     * @param request The HTTP request we are processing
+     * @param response The HTTP response we are creating
+     *
+     * @exception Exception if the application business logic throws
+     *  an exception
+     */
+    public ActionForward execute(ActionMapping mapping,
+				 ActionForm form,
+				 HttpServletRequest request,
+				 HttpServletResponse response)
+	throws Exception {
+
+	// Extract attributes and parameters we will need
+	Locale locale = getLocale(request);
+	MessageResources messages = getResources(request);
+	HttpSession session = request.getSession();
+	SubscriptionForm subform = (SubscriptionForm) form;
+	String action = subform.getAction();
+	if (action == null) {
+	    action = "?";
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("SaveSubscriptionAction:  Processing " + action +
+                      " action");
+        }
+
+	// Is there a currently logged on user?
+	User user = (User) session.getAttribute(Constants.USER_KEY);
+	if (user == null) {
+            if (log.isTraceEnabled()) {
+                log.trace(" User is not logged on in session "
+                          + session.getId());
+            }
+	    return (mapping.findForward("logon"));
+        }
+
+	// Was this transaction cancelled?
+	if (isCancelled(request)) {
+            if (log.isTraceEnabled()) {
+                log.trace(" Transaction '" + action +
+                          "' was cancelled");
+            }
+            session.removeAttribute(Constants.SUBSCRIPTION_KEY);
+	    return (mapping.findForward("success"));
+	}
+
+	// Is there a related Subscription object?
+	Subscription subscription =
+	  (Subscription) session.getAttribute(Constants.SUBSCRIPTION_KEY);
+        if ("Create".equals(action)) {
+            if (log.isTraceEnabled()) {
+                log.trace(" Creating subscription for mail server '" +
+                          subform.getHost() + "'");
+            }
+            subscription =
+                user.createSubscription(subform.getHost());
+        }
+	if (subscription == null) {
+            if (log.isTraceEnabled()) {
+                log.trace(" Missing subscription for user '" +
+                          user.getUsername() + "'");
+            }
+	    response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+	                       messages.getMessage("error.noSubscription"));
+	    return (null);
+	}
+
+	// Was this transaction a Delete?
+	if (action.equals("Delete")) {
+            if (log.isTraceEnabled()) {
+                log.trace(" Deleting mail server '" +
+                          subscription.getHost() + "' for user '" +
+                          user.getUsername() + "'");
+            }
+            user.removeSubscription(subscription);
+	    session.removeAttribute(Constants.SUBSCRIPTION_KEY);
+            try {
+                UserDatabase database = (UserDatabase)
+                    servlet.getServletContext().
+                    getAttribute(Constants.DATABASE_KEY);
+                database.save();
+            } catch (Exception e) {
+                log.error("Database save", e);
+            }
+	    return (mapping.findForward("success"));
+	}
+
+	// All required validations were done by the form itself
+
+	// Update the persistent subscription information
+        if (log.isTraceEnabled()) {
+            log.trace(" Populating database from form bean");
+        }
+        try {
+            PropertyUtils.copyProperties(subscription, subform);
+        } catch (InvocationTargetException e) {
+            Throwable t = e.getTargetException();
+            if (t == null)
+                t = e;
+            log.error("Subscription.populate", t);
+            throw new ServletException("Subscription.populate", t);
+        } catch (Throwable t) {
+            log.error("Subscription.populate", t);
+            throw new ServletException("Subscription.populate", t);
+        }
+
+        try {
+            UserDatabase database = (UserDatabase)
+                servlet.getServletContext().
+                getAttribute(Constants.DATABASE_KEY);
+            database.save();
+        } catch (Exception e) {
+            log.error("Database save", e);
+        }
+
+	// Remove the obsolete form bean and current subscription
+	if (mapping.getAttribute() != null) {
+            if ("request".equals(mapping.getScope()))
+                request.removeAttribute(mapping.getAttribute());
+            else
+                session.removeAttribute(mapping.getAttribute());
+        }
+	session.removeAttribute(Constants.SUBSCRIPTION_KEY);
+
+	// Forward control to the specified success URI
+        if (log.isTraceEnabled()) {
+            log.trace(" Forwarding to success page");
+        }
+	return (mapping.findForward("success"));
+
+    }
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/Subscription.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/Subscription.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/Subscription.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/Subscription.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,103 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example;
+
+
+/**
+ * <p>A <strong>Subscription</strong> which is stored, along with the
+ * associated {@link User}, in a {@link UserDatabase}.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $
+ */
+
+public interface Subscription {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the auto-connect flag.
+     */
+    public boolean getAutoConnect();
+
+
+    /**
+     * Set the auto-connect flag.
+     *
+     * @param autoConnect The new auto-connect flag
+     */
+    public void setAutoConnect(boolean autoConnect);
+
+
+    /**
+     * Return the host name.
+     */
+    public String getHost();
+
+
+    /**
+     * Return the password.
+     */
+    public String getPassword();
+
+
+    /**
+     * Set the password.
+     *
+     * @param password The new password
+     */
+    public void setPassword(String password);
+
+
+    /**
+     * Return the subscription type.
+     */
+    public String getType();
+
+
+    /**
+     * Set the subscription type.
+     *
+     * @param type The new subscription type
+     */
+    public void setType(String type);
+
+
+    /**
+     * Return the {@link User} owning this Subscription.
+     */
+    public User getUser();
+
+
+    /**
+     * Return the username.
+     */
+    public String getUsername();
+
+
+    /**
+     * Set the username.
+     *
+     * @param username The new username
+     */
+    public void setUsername(String username);
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SubscriptionForm.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SubscriptionForm.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SubscriptionForm.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/SubscriptionForm.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,302 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example;
+
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionMapping;
+
+
+/**
+ * Form bean for the user profile page.  This form has the following fields,
+ * with default values in square brackets:
+ * <ul>
+ * <li><b>action</b> - The maintenance action we are performing (Create, Delete,
+ *     or Edit).
+ * <li><b>host</b> - The mail host for this subscription.  [REQUIRED]
+ * <li><b>password</b> - The password for this subscription.  [REQUIRED]
+ * <li><b>type</b> - The subscription type (imap,pop3)
+       for this subscription.  [REQUIRED]
+ * <li><b>username</b> - The username of this subscription.  [REQUIRED]
+ * </ul>
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $
+ */
+
+public final class SubscriptionForm extends ActionForm  {
+
+
+    // --------------------------------------------------- Instance Variables
+
+
+    /**
+     * The maintenance action we are performing (Create or Edit).
+     */
+    private String action = "Create";
+
+
+    /**
+     * Should we auto-connect at startup time?
+     */
+    private boolean autoConnect = false;
+
+
+    /**
+     * The host name.
+     */
+    private String host = null;
+
+
+    /**
+     * The password.
+     */
+    private String password = null;
+
+
+    /**
+     * The subscription type.
+     */
+    private String type = null;
+
+
+    /**
+     * The username.
+     */
+    private String username = null;
+
+
+    // ----------------------------------------------------------- Properties
+
+
+    /**
+     * Return the maintenance action.
+     */
+    public String getAction() {
+
+	return (this.action);
+
+    }
+
+
+    /**
+     * Set the maintenance action.
+     *
+     * @param action The new maintenance action.
+     */
+    public void setAction(String action) {
+
+        this.action = action;
+
+    }
+
+
+    /**
+     * Return the auto-connect flag.
+     */
+    public boolean getAutoConnect() {
+
+        return (this.autoConnect);
+
+    }
+
+
+    /**
+     * Set the auto-connect flag.
+     *
+     * @param autoConnect The new auto-connect flag
+     */
+    public void setAutoConnect(boolean autoConnect) {
+
+        this.autoConnect = autoConnect;
+    }
+
+
+    /**
+     * Return the host name.
+     */
+    public String getHost() {
+
+	return (this.host);
+
+    }
+
+
+    /**
+     * Set the host name.
+     *
+     * @param host The host name
+     */
+    public void setHost(String host) {
+
+        this.host = host;
+
+    }
+
+
+    /**
+     * Return the password.
+     */
+    public String getPassword() {
+
+	return (this.password);
+
+    }
+
+
+    /**
+     * Set the password.
+     *
+     * @param password The new password
+     */
+    public void setPassword(String password) {
+
+        this.password = password;
+
+    }
+
+
+    /**
+     * Return the subscription type.
+     */
+    public String getType() {
+
+	return (this.type);
+
+    }
+
+
+    /**
+     * Set the subscription type.
+     *
+     * @param type The subscription type
+     */
+    public void setType(String type) {
+
+        this.type = type;
+
+    }
+
+
+    /**
+     * Return the username.
+     */
+    public String getUsername() {
+
+	return (this.username);
+
+    }
+
+
+    /**
+     * Set the username.
+     *
+     * @param username The new username
+     */
+    public void setUsername(String username) {
+
+        this.username = username;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Reset all properties to their default values.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public void reset(ActionMapping mapping, HttpServletRequest request) {
+
+        this.action = "Create";
+        this.autoConnect = false;
+        this.host = null;
+        this.password = null;
+        this.type = null;
+        this.username = null;
+
+    }
+
+
+    /**
+     * Validate the properties that have been set from this HTTP request,
+     * and return an <code>ActionErrors</code> object that encapsulates any
+     * validation errors that have been found.  If no errors are found, return
+     * <code>null</code> or an <code>ActionErrors</code> object with no
+     * recorded error messages.
+     *
+     * @param mapping The mapping used to select this instance
+     * @param request The servlet request we are processing
+     */
+    public ActionErrors validate(ActionMapping mapping,
+                                 HttpServletRequest request) {
+
+        ActionErrors errors = new ActionErrors();
+
+	if ((host == null) || (host.length() < 1))
+            errors.add("host",
+                       new ActionError("error.host.required"));
+	if ((username == null) || (username.length() < 1))
+            errors.add("username",
+                       new ActionError("error.username.required"));
+	if ((password == null) || (password.length() < 1))
+            errors.add("password",
+                       new ActionError("error.password.required"));
+	if ((type == null) || (type.length() < 1))
+            errors.add("type",
+                       new ActionError("error.type.required"));
+	else if (!"imap".equals(type) && !"pop3".equals(type))
+            errors.add("type",
+                       new ActionError("error.type.invalid", type));
+
+	return (errors);
+
+    }
+
+
+    /**
+     * <p>Return a string representation of this form bean.</p>
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer(super.toString());
+        sb.append(",action=");
+        sb.append(action);
+        sb.append(",autoConnect=");
+        sb.append(autoConnect);
+        sb.append(",host=");
+        sb.append(host);
+        sb.append(",password=");
+        sb.append(password);
+        sb.append(",type=");
+        sb.append(type);
+        sb.append(",username=");
+        sb.append(username);
+        return (sb.toString());
+
+    }
+
+
+}
+

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/User.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/User.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/User.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/User.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,147 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example;
+
+
+/**
+ * <p>A <strong>User</strong> which is stored, along with his or her
+ * associated {@link Subscription}s, in a {@link UserDatabase}.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $
+ * @since Struts 1.1
+ */
+
+public interface User {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the {@link UserDatabase} with which we are associated.
+     */
+    public UserDatabase getDatabase();
+
+
+    /**
+     * Return the from address.
+     */
+    public String getFromAddress();
+
+
+    /**
+     * Set the from address.
+     *
+     * @param fromAddress The new from address
+     */
+    public void setFromAddress(String fromAddress);
+
+
+    /**
+     * Return the full name.
+     */
+    public String getFullName();
+
+
+    /**
+     * Set the full name.
+     *
+     * @param fullName The new full name
+     */
+    public void setFullName(String fullName);
+
+
+    /**
+     * Return the password.
+     */
+    public String getPassword();
+
+
+    /**
+     * Set the password.
+     *
+     * @param password The new password
+     */
+    public void setPassword(String password);
+
+
+    /**
+     * Return the reply-to address.
+     */
+    public String getReplyToAddress();
+
+
+    /**
+     * Set the reply-to address.
+     *
+     * @param replyToAddress The new reply-to address
+     */
+    public void setReplyToAddress(String replyToAddress);
+
+
+    /**
+     * Find and return all {@link Subscription}s associated with this user.
+     * If there are none, a zero-length array is returned.
+     */
+    public Subscription[] getSubscriptions();
+
+
+    /**
+     * Return the username.
+     */
+    public String getUsername();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Create and return a new {@link Subscription} associated with this
+     * User, for the specified host name.
+     *
+     * @param host Host name for which to create a subscription
+     *
+     * @exception IllegalArgumentException if the host name is not unique
+     *  for this user
+     */
+    public Subscription createSubscription(String host);
+
+
+    /**
+     * Find and return the {@link Subscription} associated with the specified
+     * host.  If none is found, return <code>null</code>.
+     *
+     * @param host Host name to look up
+     */
+    public Subscription findSubscription(String host);
+
+
+    /**
+     * Remove the specified {@link Subscription} from being associated
+     * with this User.
+     *
+     * @param subscription Subscription to be removed
+     *
+     * @exception IllegalArgumentException if the specified subscription is not
+     *  associated with this User
+     */
+    public void removeSubscription(Subscription subscription);
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/UserDatabase.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/UserDatabase.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/UserDatabase.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/UserDatabase.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example;
+
+
+/**
+ * <p>A <strong>Data Access Object</strong> (DAO) interface describing
+ * the available operations for retrieving and storing {@link User}s
+ * (and their associated {@link Subscription}s) in some persistence layer
+ * whose characteristics are not specified here.  One or more implementations
+ * will be created to perform the actual I/O that is required.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $
+ * @since Struts 1.1
+ */
+
+public interface UserDatabase {
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Create and return a new {@link User} defined in this user database.
+     * </p>
+     *
+     * @param username Username of the new user
+     *
+     * @exception IllegalArgumentExceptionif the specified username
+     *  is not unique
+     */
+    public User createUser(String username);
+
+
+    /**
+     * <p>Finalize access to the underlying persistence layer.</p>
+     *
+     * @exception Exception if a database access error occurs
+     */
+    public void close() throws Exception;
+
+
+    /**
+     * <p>Return the existing {@link User} with the specified username,
+     * if any; otherwise return <code>null</code>.</p>
+     *
+     * @param username Username of the user to retrieve
+     */
+    public User findUser(String username);
+
+
+    /**
+     * <p>Return the set of {@link User}s defined in this user database.</p>
+     */
+    public User[] findUsers();
+
+
+    /**
+     * <p>Initiate access to the underlying persistence layer.</p>
+     *
+     * @exception Exception if a database access error occurs
+     */
+    public void open() throws Exception;
+
+
+    /**
+     * Remove the specified {@link User} from this database.
+     *
+     * @param user User to be removed
+     *
+     * @exception IllegalArgumentException if the specified user is not
+     *  associated with this database
+     */
+    public void removeUser(User user);
+
+
+    /**
+     * <p>Save any pending changes to the underlying persistence layer.</p>
+     *
+     * @exception Exception if a database access error occurs
+     */
+    public void save() throws Exception;
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryDatabasePlugIn.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryDatabasePlugIn.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryDatabasePlugIn.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryDatabasePlugIn.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,247 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example.memory;
+
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import javax.servlet.ServletException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.action.ActionServlet;
+import org.apache.struts.action.PlugIn;
+import org.apache.struts.config.ModuleConfig;
+import org.apache.struts.util.LabelValueBean;
+import org.apache.struts.webapp.example.Constants;
+
+/**
+ * <p><strong>MemoryDatabasePlugIn</strong> initializes and finalizes the
+ * persistent storage of User and Subscription information for the Struts
+ * Demonstration Application, using an in-memory database backed by an
+ * XML file.</p>
+ *
+ * <p><strong>IMPLEMENTATION WARNING</strong> - If this web application is run
+ * from a WAR file, or in another environment where reading and writing of the
+ * web application resource is impossible, the initial contents will be copied
+ * to a file in the web application temporary directory provided by the
+ * container.  This is for demonstration purposes only - you should
+ * <strong>NOT</strong> assume that files written here will survive a restart
+ * of your servlet container.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $
+ */
+
+public final class MemoryDatabasePlugIn implements PlugIn {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The application configuration for our owning module.
+     */
+    private ModuleConfig config = null;
+
+
+    /**
+     * The {@link MemoryUserDatabase} object we construct and make available.
+     */
+    private MemoryUserDatabase database = null;
+
+
+    /**
+     * Logging output for this plug in instance.
+     */
+    private Log log = LogFactory.getLog(this.getClass());
+
+
+    /**
+     * The {@link ActionServlet} owning this application.
+     */
+    private ActionServlet servlet = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The web application resource path of our persistent database
+     * storage file.
+     */
+    private String pathname = "/WEB-INF/database.xml";
+
+    public String getPathname() {
+        return (this.pathname);
+    }
+
+    public void setPathname(String pathname) {
+        this.pathname = pathname;
+    }
+
+
+    // --------------------------------------------------------- PlugIn Methods
+
+
+    /**
+     * Gracefully shut down this database, releasing any resources
+     * that were allocated at initialization.
+     */
+    public void destroy() {
+
+        log.info("Finalizing memory database plug in");
+
+        if (database != null) {
+            try {
+                database.close();
+            } catch (Exception e) {
+                log.error("Closing memory database", e);
+            }
+        }
+
+	servlet.getServletContext().removeAttribute(Constants.DATABASE_KEY);
+        database = null;
+        servlet = null;
+        database = null;
+        config = null;
+
+    }
+
+
+    /**
+     * Initialize and load our initial database from persistent storage.
+     *
+     * @param servlet The ActionServlet for this web application
+     * @param config The ApplicationConfig for our owning module
+     *
+     * @exception ServletException if we cannot configure ourselves correctly
+     */
+    public void init(ActionServlet servlet, ModuleConfig config)
+        throws ServletException {
+
+        log.info("Initializing memory database plug in from '" +
+                 pathname + "'");
+
+        // Remember our associated configuration and servlet
+        this.config = config;
+        this.servlet = servlet;
+
+        // Construct a new database and make it available
+        database = new MemoryUserDatabase();
+        try {
+            String path = calculatePath();
+            if (log.isDebugEnabled()) {
+                log.debug(" Loading database from '" + path + "'");
+            }
+            database.setPathname(path);
+            database.open();
+        } catch (Exception e) {
+            log.error("Opening memory database", e);
+            throw new ServletException("Cannot load database from '" +
+                                       pathname + "'", e);
+        }
+
+        // Make the initialized database available
+        servlet.getServletContext().setAttribute(Constants.DATABASE_KEY,
+                                                 database);
+
+        // Setup and cache other required data
+        setupCache(servlet, config);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * <p>Cache commonly required data as servlet context attributes.</p>
+     *
+     * @param servlet The <code>ActionServlet</code> instance running
+     *  this webapp
+     * @param config The <code>ModuleConfig</code> for this application module
+     */
+    protected void setupCache(ActionServlet servlet, ModuleConfig config) {
+
+        // Set up list of server types under "serverTypes"
+        ArrayList serverTypes = new ArrayList();
+        serverTypes.add(new LabelValueBean("IMAP Protocol", "imap"));
+        serverTypes.add(new LabelValueBean("POP3 Protocol", "pop3"));
+        servlet.getServletContext().setAttribute("serverTypes", serverTypes);
+
+    }
+
+
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Calculate and return an absolute pathname to the XML file to contain
+     * our persistent storage information.
+     *
+     * @exception Exception if an input/output error occurs
+     */
+    private String calculatePath() throws Exception {
+
+        // Can we access the database via file I/O?
+        String path = servlet.getServletContext().getRealPath(pathname);
+        if (path != null) {
+            return (path);
+        }
+
+        // Does a copy of this file already exist in our temporary directory
+        File dir = (File)
+            servlet.getServletContext().getAttribute
+            ("javax.servlet.context.tempdir");
+        File file = new File(dir, "struts-example-database.xml");
+        if (file.exists()) {
+            return (file.getAbsolutePath());
+        }
+
+        // Copy the static resource to a temporary file and return its path
+        InputStream is =
+            servlet.getServletContext().getResourceAsStream(pathname);
+        BufferedInputStream bis = new BufferedInputStream(is, 1024);
+        FileOutputStream os =
+            new FileOutputStream(file);
+        BufferedOutputStream bos = new BufferedOutputStream(os, 1024);
+        byte buffer[] = new byte[1024];
+        while (true) {
+            int n = bis.read(buffer);
+            if (n <= 0) {
+                break;
+            }
+            bos.write(buffer, 0, n);
+        }
+        bos.close();
+        bis.close();
+        return (file.getAbsolutePath());
+
+    }
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemorySubscription.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemorySubscription.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemorySubscription.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemorySubscription.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,180 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example.memory;
+
+
+import org.apache.struts.webapp.example.Subscription;
+import org.apache.struts.webapp.example.User;
+
+
+/**
+ * <p>Concrete implementation of {@link Subscription} for an in-memory
+ * database backed by an XML data file.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $
+ * @since Struts 1.1
+ */
+
+public final class MemorySubscription implements Subscription {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * <p>Construct a new Subscription associated with the specified
+     * {@link User}.
+     *
+     * @param user The user with which we are associated
+     * @param host The mail host for this subscription
+     */
+    public MemorySubscription(MemoryUser user, String host) {
+
+        super();
+        this.user = user;
+        this.host = host;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The mail host for this subscription.
+     */
+    private String host = null;
+
+
+    /**
+     * The {@link User} with which we are associated.
+     */
+    private MemoryUser user = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Should we auto-connect at startup time?
+     */
+    private boolean autoConnect = false;
+
+    public boolean getAutoConnect() {
+        return (this.autoConnect);
+    }
+
+    public void setAutoConnect(boolean autoConnect) {
+        this.autoConnect = autoConnect;
+    }
+
+
+    /**
+     * The mail host for this subscription.
+     */
+    public String getHost() {
+        return (this.host);
+    }
+
+
+    /**
+     * The password (in clear text) for this subscription.
+     */
+    private String password = null;
+
+    public String getPassword() {
+        return (this.password);
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+
+    /**
+     * The subscription type ("imap" or "pop3").
+     */
+    private String type = "imap";
+
+    public String getType() {
+        return (this.type);
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+
+    /**
+     * The User owning this Subscription.
+     */
+    public User getUser() {
+        return (this.user);
+    }
+
+
+    /**
+     * The username for this subscription.
+     */
+    private String username = null;
+
+    public String getUsername() {
+        return (this.username);
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("<subscription host=\"");
+        sb.append(host);
+        sb.append("\" autoConnect=\"");
+        sb.append(autoConnect);
+        sb.append("\"");
+        if (password != null) {
+            sb.append(" password=\"");
+            sb.append(password);
+            sb.append("\"");
+        }
+        if (type != null) {
+            sb.append(" type=\"");
+            sb.append(type);
+            sb.append("\"");
+        }
+        if (username != null) {
+            sb.append(" username=\"");
+            sb.append(username);
+            sb.append("\"");
+        }
+        sb.append(">");
+        return (sb.toString());
+
+    }
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryUser.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryUser.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryUser.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryUser.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,270 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example.memory;
+
+
+import java.util.HashMap;
+import org.apache.struts.webapp.example.Subscription;
+import org.apache.struts.webapp.example.User;
+import org.apache.struts.webapp.example.UserDatabase;
+
+
+/**
+ * <p>Concrete implementation of {@link User} for an in-memory
+ * database backed by an XML data file.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $
+ * @since Struts 1.1
+ */
+
+public final class MemoryUser implements User {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * <p>Construct a new User associated with the specified
+     * {@link UserDatabase}.
+     *
+     * @param database The user database with which we are associated
+     * @param username The username of this user
+     */
+    public MemoryUser(MemoryUserDatabase database, String username) {
+
+        super();
+        this.database = database;
+        this.username = username;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The {@link UserDatabase} with which we are associated.
+     */
+    private MemoryUserDatabase database = null;
+
+
+    /**
+     * The {@link Subscription}s for this User, keyed by hostname.
+     */
+    private HashMap subscriptions = new HashMap();
+
+
+    /**
+     * The username for this user.
+     */
+    private String username = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The {@link UserDatabase} with which we are associated.
+     */
+    public UserDatabase getDatabase() {
+        return (this.database);
+    }
+
+
+    /**
+     * The email address from which messages are sent.
+     */
+    private String fromAddress = null;
+
+    public String getFromAddress() {
+        return (this.fromAddress);
+    }
+
+    public void setFromAddress(String fromAddress) {
+        this.fromAddress = fromAddress;
+    }
+
+
+    /**
+     * The full name of this user, included in from addresses.
+     */
+    private String fullName = null;
+
+    public String getFullName() {
+        return (this.fullName);
+    }
+
+    public void setFullName(String fullName) {
+        this.fullName = fullName;
+    }
+
+
+    /**
+     * The password (in clear text).
+     */
+    private String password = null;
+
+    public String getPassword() {
+        return (this.password);
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+
+    /**
+     * The EMAIL address to which replies should be sent.
+     */
+    private String replyToAddress = null;
+
+    public String getReplyToAddress() {
+        return (this.replyToAddress);
+    }
+
+    public void setReplyToAddress(String replyToAddress) {
+        this.replyToAddress = replyToAddress;
+    }
+
+
+    /**
+     * Find and return all {@link Subscription}s associated with this user.
+     * If there are none, a zero-length array is returned.
+     */
+    public Subscription[] getSubscriptions() {
+
+        synchronized (subscriptions) {
+            Subscription results[] = new Subscription[subscriptions.size()];
+            return ((Subscription[]) subscriptions.values().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * The username (must be unique).
+     */
+    public String getUsername() {
+        return (this.username);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Create and return a new {@link Subscription} associated with this
+     * User, for the specified host name.
+     *
+     * @param host Host name for which to create a subscription
+     *
+     * @exception IllegalArgumentException if the host name is not unique
+     *  for this user
+     */
+    public Subscription createSubscription(String host) {
+
+        synchronized (subscriptions) {
+            if (subscriptions.get(host) != null) {
+                throw new IllegalArgumentException("Duplicate host '" + host
+                                                   + "' for user '" +
+                                                   username + "'");
+            }
+            MemorySubscription subscription =
+                new MemorySubscription(this, host);
+            synchronized (subscriptions) {
+                subscriptions.put(host, subscription);
+            }
+            return (subscription);
+        }
+
+    }
+
+
+    /**
+     * Find and return the {@link Subscription} associated with the specified
+     * host.  If none is found, return <code>null</code>.
+     *
+     * @param host Host name to look up
+     */
+    public Subscription findSubscription(String host) {
+
+        synchronized (subscriptions) {
+            return ((Subscription) subscriptions.get(host));
+        }
+
+    }
+
+
+    /**
+     * Remove the specified {@link Subscription} from being associated
+     * with this User.
+     *
+     * @param subscription Subscription to be removed
+     *
+     * @exception IllegalArgumentException if the specified subscription is not
+     *  associated with this User
+     */
+    public void removeSubscription(Subscription subscription) {
+
+        if (!(this == subscription.getUser())) {
+            throw new IllegalArgumentException
+                ("Subscription not associated with this user");
+        }
+        synchronized (subscriptions) {
+            subscriptions.remove(subscription.getHost());
+        }
+
+    }
+
+
+    /**
+     * Return a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("<user username=\"");
+        sb.append(username);
+        sb.append("\"");
+        if (fromAddress != null) {
+            sb.append(" fromAddress=\"");
+            sb.append(fromAddress);
+            sb.append("\"");
+        }
+        if (fullName != null) {
+            sb.append(" fullName=\"");
+            sb.append(fullName);
+            sb.append("\"");
+        }
+        if (password != null) {
+            sb.append(" password=\"");
+            sb.append(password);
+            sb.append("\"");
+        }
+        if (replyToAddress != null) {
+            sb.append(" replyToAddress=\"");
+            sb.append(replyToAddress);
+            sb.append("\"");
+        }
+        sb.append(">");
+        return (sb.toString());
+
+    }
+
+
+}

Added: struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryUserDatabase.java
URL: http://svn.apache.org/viewcvs/struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryUserDatabase.java?rev=392100&view=auto
==============================================================================
--- struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryUserDatabase.java (added)
+++ struts/action/trunk/apps/faces-example1/src/main/java/org/apache/struts/webapp/example/memory/MemoryUserDatabase.java Thu Apr  6 15:31:36 2006
@@ -0,0 +1,412 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.struts.webapp.example.memory;
+
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import org.apache.commons.digester.Digester;
+import org.apache.commons.digester.ObjectCreationFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.webapp.example.Subscription;
+import org.apache.struts.webapp.example.User;
+import org.apache.struts.webapp.example.UserDatabase;
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p>Concrete implementation of {@link UserDatabase} for an in-memory
+ * database backed by an XML data file.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $
+ * @since Struts 1.1
+ */
+
+public final class MemoryUserDatabase implements UserDatabase {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Logging output for this user database instance.
+     */
+    private Log log = LogFactory.getLog(this.getClass());
+
+
+    /**
+     * The {@link User}s associated with this UserDatabase, keyed by username.
+     */
+    private HashMap users = new HashMap();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Absolute pathname to the persistent file we use for loading and storing
+     * persistent data.
+     */
+    private String pathname = null;
+
+    private String pathnameOld = null;
+
+    private String pathnameNew = null;
+
+    public String getPathname() {
+        return (this.pathname);
+    }
+
+    public void setPathname(String pathname) {
+        this.pathname = pathname;
+        pathnameOld = pathname + ".old";
+        pathnameNew = pathname + ".new";
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Finalize access to the underlying persistence layer.</p>
+     *
+     * @exception Exception if a database access error occurs
+     */
+    public void close() throws Exception {
+
+        save();
+
+    }
+
+
+    /**
+     * <p>Create and return a new {@link User} defined in this user database.
+     * </p>
+     *
+     * @param username Username of the new user
+     *
+     * @exception IllegalArgumentExceptionif the specified username
+     *  is not unique
+     */
+    public User createUser(String username) {
+
+        synchronized (users) {
+            if (users.get(username) != null) {
+                throw new IllegalArgumentException("Duplicate user '" +
+                                                   username + "'");
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("Creating user '" + username + "'");
+            }
+            MemoryUser user = new MemoryUser(this, username);
+            synchronized (users) {
+                users.put(username, user);
+            }
+            return (user);
+        }
+
+    }
+
+
+    /**
+     * <p>Return the existing {@link User} with the specified username,
+     * if any; otherwise return <code>null</code>.</p>
+     *
+     * @param username Username of the user to retrieve
+     */
+    public User findUser(String username) {
+
+        synchronized (users) {
+            return ((User) users.get(username));
+        }
+
+    }
+
+
+    /**
+     * <p>Return the set of {@link User}s defined in this user database.</p>
+     */
+    public User[] findUsers() {
+
+        synchronized (users) {
+            User results[] = new User[users.size()];
+            return ((User[]) users.values().toArray(results));
+        }
+
+    }
+
+
+    /**
+     * <p>Initiate access to the underlying persistence layer.</p>
+     *
+     * @exception Exception if a database access error occurs
+     */
+    public void open() throws Exception {
+
+        FileInputStream fis = null;
+        BufferedInputStream bis = null;
+
+        try {
+
+            // Acquire an input stream to our database file
+            if (log.isDebugEnabled()) {
+                log.debug("Loading database from '" + pathname + "'");
+            }
+            fis = new FileInputStream(pathname);
+            bis = new BufferedInputStream(fis);
+
+            // Construct a digester to use for parsing
+            Digester digester = new Digester();
+            digester.push(this);
+            digester.setValidating(false);
+            digester.addFactoryCreate
+                ("database/user",
+                 new MemoryUserCreationFactory(this));
+            digester.addFactoryCreate
+                ("database/user/subscription",
+                 new MemorySubscriptionCreationFactory(this));
+
+            // Parse the input stream to initialize our database
+            digester.parse(bis);
+            bis.close();
+            bis = null;
+            fis = null;
+
+        } catch (Exception e) {
+
+            log.error("Loading database from '" + pathname + "':", e);
+            throw e;
+
+        } finally {
+
+            if (bis != null) {
+                try {
+                    bis.close();
+                } catch (Throwable t) {
+                    ;
+                }
+                bis = null;
+                fis = null;
+            }
+
+        }
+
+    }
+
+
+    /**
+     * Remove the specified {@link User} from this database.
+     *
+     * @param user User to be removed
+     *
+     * @exception IllegalArgumentException if the specified user is not
+     *  associated with this database
+     */
+    public void removeUser(User user) {
+
+        if (!(this == user.getDatabase())) {
+            throw new IllegalArgumentException
+                ("User not associated with this database");
+        }
+        if (log.isTraceEnabled()) {
+            log.trace("Removing user '" + user.getUsername() + "'");
+        }
+        synchronized (users) {
+            users.remove(user.getUsername());
+        }
+
+    }
+
+
+    /**
+     * <p>Save any pending changes to the underlying persistence layer.</p>
+     *
+     * @exception Exception if a database access error occurs
+     */
+    public void save() throws Exception {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Saving database to '" + pathname + "'");
+        }
+        File fileNew = new File(pathnameNew);
+        PrintWriter writer = null;
+
+        try {
+
+            // Configure our PrintWriter
+            FileOutputStream fos = new FileOutputStream(fileNew);
+            OutputStreamWriter osw = new OutputStreamWriter(fos);
+            writer = new PrintWriter(osw);
+
+            // Print the file prolog
+            writer.println("<?xml version='1.0'?>");
+            writer.println("<database>");
+
+            // Print entries for each defined user and associated subscriptions
+            User users[] = findUsers();
+            for (int i = 0; i < users.length; i++) {
+                writer.print("  ");
+                writer.println(users[i]);
+                Subscription subscriptions[] =
+                    users[i].getSubscriptions();
+                for (int j = 0; j < subscriptions.length; j++) {
+                    writer.print("    ");
+                    writer.println(subscriptions[j]);
+                    writer.print("    ");
+                    writer.println("</subscription>");
+                }
+                writer.print("  ");
+                writer.println("</user>");
+            }
+
+            // Print the file epilog
+            writer.println("</database>");
+
+            // Check for errors that occurred while printing
+            if (writer.checkError()) {
+                writer.close();
+                fileNew.delete();
+                throw new IOException
+                    ("Saving database to '" + pathname + "'");
+            }
+            writer.close();
+            writer = null;
+
+        } catch (IOException e) {
+
+            if (writer != null) {
+                writer.close();
+            }
+            fileNew.delete();
+            throw e;
+
+        }
+
+
+        // Perform the required renames to permanently save this file
+        File fileOrig = new File(pathname);
+        File fileOld = new File(pathnameOld);
+        if (fileOrig.exists()) {
+            fileOld.delete();
+            if (!fileOrig.renameTo(fileOld)) {
+                throw new IOException
+                    ("Renaming '" + pathname + "' to '" + pathnameOld + "'");
+            }
+        }
+        if (!fileNew.renameTo(fileOrig)) {
+            if (fileOld.exists()) {
+                fileOld.renameTo(fileOrig);
+            }
+            throw new IOException
+                ("Renaming '" + pathnameNew + "' to '" + pathname + "'");
+        }
+        fileOld.delete();
+
+    }
+
+
+}
+
+
+/**
+ * Digester object creation factory for subscription instances.
+ */
+class MemorySubscriptionCreationFactory implements ObjectCreationFactory {
+
+    public MemorySubscriptionCreationFactory(MemoryUserDatabase database) {
+        this.database = database;
+    }
+
+    private MemoryUserDatabase database = null;
+
+    private Digester digester = null;
+
+    public Digester getDigester() {
+        return (this.digester);
+    }
+
+    public void setDigester(Digester digester) {
+        this.digester = digester;
+    }
+
+    public Object createObject(Attributes attributes) {
+        String host = attributes.getValue("host");
+        User user = (User) digester.peek();
+        Subscription subscription = user.createSubscription(host);
+        String autoConnect = attributes.getValue("autoConnect");
+        if (autoConnect == null) {
+            autoConnect = "false";
+        }
+        if ("true".equalsIgnoreCase(autoConnect) ||
+            "yes".equalsIgnoreCase(autoConnect)) {
+            subscription.setAutoConnect(true);
+        } else {
+            subscription.setAutoConnect(false);
+        }
+        subscription.setPassword(attributes.getValue("password"));
+        subscription.setType(attributes.getValue("type"));
+        subscription.setUsername(attributes.getValue("username"));
+        return (subscription);
+    }
+
+}
+
+
+/**
+ * Digester object creation factory for user instances.
+ */
+class MemoryUserCreationFactory implements ObjectCreationFactory {
+
+    public MemoryUserCreationFactory(MemoryUserDatabase database) {
+        this.database = database;
+    }
+
+    private MemoryUserDatabase database = null;
+
+    private Digester digester = null;
+
+    public Digester getDigester() {
+        return (this.digester);
+    }
+
+    public void setDigester(Digester digester) {
+        this.digester = digester;
+    }
+
+    public Object createObject(Attributes attributes) {
+        String username = attributes.getValue("username");
+        User user = database.createUser(username);
+        user.setFromAddress(attributes.getValue("fromAddress"));
+        user.setFullName(attributes.getValue("fullName"));
+        user.setPassword(attributes.getValue("password"));
+        user.setReplyToAddress(attributes.getValue("replyToAddress"));
+        return (user);
+    }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org