You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by hu...@apache.org on 2007/11/20 08:38:01 UTC

svn commit: r596559 [1/2] - in /struts/sandbox/trunk/jpa-mailreader: ./ src/ src/main/ src/main/java/ src/main/java/META-INF/ src/main/java/action/ src/main/java/action/user/ src/main/java/action/user/subscription/ src/main/java/entity/ src/main/java/e...

Author: husted
Date: Mon Nov 19 23:37:56 2007
New Revision: 596559

URL: http://svn.apache.org/viewvc?rev=596559&view=rev
Log:
WW-1399 Initial check-in of a JPA MailReader implementation (with CodeBehind). 

Added:
    struts/sandbox/trunk/jpa-mailreader/
    struts/sandbox/trunk/jpa-mailreader/LICENSE.txt
    struts/sandbox/trunk/jpa-mailreader/NOTICE.txt
    struts/sandbox/trunk/jpa-mailreader/STATUS.txt
    struts/sandbox/trunk/jpa-mailreader/src/
    struts/sandbox/trunk/jpa-mailreader/src/main/
    struts/sandbox/trunk/jpa-mailreader/src/main/java/
    struts/sandbox/trunk/jpa-mailreader/src/main/java/META-INF/
    struts/sandbox/trunk/jpa-mailreader/src/main/java/META-INF/persistence.xml
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/Index.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package.properties
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package_ja.properties
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package_ru.properties
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Create.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Index.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Login.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Logout.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Update.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Create.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Delete.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Index.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Update.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/EntityManagerHelper.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/EntityManagerSuperclass.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/EntitySuperclass.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/ExpiredPasswordException.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/protocol/
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/protocol/Protocol.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/protocol/ProtocolManager.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/protocol/ProtocolManagerInterface.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/protocol/ProtocolTypeConverter.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/subscription/
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/subscription/Subscription.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/subscription/SubscriptionManager.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/subscription/SubscriptionManagerInterface.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/subscription/SubscriptionTypeConverter.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/user/
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/user/User.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/user/UserManager.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/user/UserManagerInterface.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/user/UserTypeConverter.java
    struts/sandbox/trunk/jpa-mailreader/src/main/java/struts.xml
    struts/sandbox/trunk/jpa-mailreader/src/main/java/xwork-conversion.properties
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/META-INF/
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/META-INF/MANIFEST.MF
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/WEB-INF/
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/WEB-INF/web.xml
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/assets/
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/assets/focus-first-input.js
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/index.html
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/index.jsp
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/user/
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/user/@form.jsp
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/user/create-input.jsp
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/user/index.jsp
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/user/login-input.jsp
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/user/subscription/
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/user/subscription/@form.jsp
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/user/subscription/create-input.jsp
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/user/subscription/delete-input.jsp
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/user/subscription/update-input.jsp
    struts/sandbox/trunk/jpa-mailreader/src/main/webapp/user/update-input.jsp
    struts/sandbox/trunk/jpa-mailreader/src/test/
    struts/sandbox/trunk/jpa-mailreader/src/test/java/
    struts/sandbox/trunk/jpa-mailreader/src/test/java/action/
    struts/sandbox/trunk/jpa-mailreader/src/test/java/action/HelloTest.java
    struts/sandbox/trunk/jpa-mailreader/src/test/java/action/RetainTest.java
    struts/sandbox/trunk/jpa-mailreader/src/test/java/entity/

Added: struts/sandbox/trunk/jpa-mailreader/LICENSE.txt
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/LICENSE.txt?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/LICENSE.txt (added)
+++ struts/sandbox/trunk/jpa-mailreader/LICENSE.txt Mon Nov 19 23:37:56 2007
@@ -0,0 +1,174 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.

Added: struts/sandbox/trunk/jpa-mailreader/NOTICE.txt
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/NOTICE.txt?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/NOTICE.txt (added)
+++ struts/sandbox/trunk/jpa-mailreader/NOTICE.txt Mon Nov 19 23:37:56 2007
@@ -0,0 +1,6 @@
+Apache Struts
+
+Copyright 2006 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/

Added: struts/sandbox/trunk/jpa-mailreader/STATUS.txt
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/STATUS.txt?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/STATUS.txt (added)
+++ struts/sandbox/trunk/jpa-mailreader/STATUS.txt Mon Nov 19 23:37:56 2007
@@ -0,0 +1,123 @@
+JPA MailReader
+------------------------------------------------------------------------
+
+Quality Grade: test-build only.
+
+----
+
+The JPA MailReader is a best-practices example of using Struts 2 with 
+the Java Persistence API with a standard SQL database. 
+
+The example is based on the original MailReader Demonstration 
+Application and uses a modified version of the MailReader JPA package 
+developed for the Apache Shale Example. 
+
+The application uses the Struts 2 CodeBehind plugin that eliminates  
+documenting Actions in XML. The Action classes and pages are linked 
+together using convention over configuration. In some cases, 
+Result annotations are used to move between workflows. 
+
+This implementaion expresses the business classes in an "entity" 
+package. The business classes are designed from the ground-up to be 
+used with the JPA. Sufficient annotation is provided so that a SQL 
+database schema can be generated from the entity classes. (See 
+RetainTest to bootstrap a database.)
+
+The persistence logic is contained in a "Manager" class that is 
+associated with each entity class. Essentially, the manager classes 
+are data access objects. The Managers extend a superclass which 
+provides data access code that can be shared within the persistence 
+unit. Most manager methods are type-safe wrappers around shared 
+methods. 
+
+Each business class also has an XWork type converter. Most often, 
+the converters use a substitute key for the conversion ID, rather than 
+the primary key. (Exposing the primary key to the user interface layer 
+is considered a bad practice in DBA circles.) 
+
+To provide maximum portability, the internal primary keys are UUIDs. 
+The entities are based on a mapper superclass which provides shared 
+code for creating and managing the primary keys.
+
+The business class, manager, and type converter are all stored in 
+a sub-package named for each business class. Essentially, the 
+sub-package is a resource container that encapsulates all of the 
+entity's data and behavior. 
+
+The Action packaging follows a similar strategy, but also creates 
+a resource heirarchy from the entites. The "root" action package 
+contains the "welcome" resources. The "action.user" package encapsulates
+the "user" resources, and the "action.user.subscription" package 
+encapsulates the "subscription" resources. The nesting of subscription 
+within user reflects the one-to-many relationship between a user 
+resource and and its subscriptions. 
+
+Since the CodeBehind package is being utilized, the folder structure
+for the JSP templates follows the package structure. 
+
+Each package/namespace has an Index Action, which is usually the 
+superclass for other Actions in the same package. The Actions 
+utilize the Managers to access the persistent data. The default 
+constructor for an Action instantiates the default Manager, but 
+an alternate constructor is provided that could be used to pass 
+in a mock Manager. (Accordingly, the Managers all implement a  
+specific interface.) 
+
+The Actions provide request-scope User and Subscription objects 
+that represent the resources being edited in the current request. 
+The pages record the User and/or Subscrition resource as a hidden 
+field. The type converters fetch the appropriate entity from the 
+persistence unit, so that it can be set as the current User or 
+Subscription object. 
+
+Of course, the hidden field can also be expressed as a GET 
+attribute, and this technique is used to restore the current User 
+after a redirect. Overall, the design can be considered "RESTful".
+
+The logged-in user is maintained in a session-scope "Profile".
+
+
+KNOWN ISSUES 
+
+ * No POM is provided. Dependencies include 
+   ** commons-logging-1.0.4
+   ** derbyclient
+   ** freemarker-2.3.8
+   ** ognl-2.6.11
+   ** struts-codebehind-plugin-2.0.10
+   ** struts-core-2.0.10
+   ** toplink-essentials
+   ** toplink-agent-essentials
+   ** xwork-2.0.4
+
+ * Toplink could be replaced with OpenJPA. 
+ 
+ * Need a routine to autocreate the Derby database at installation.
+ 
+ * The index.html is not redirecting to the index action. 
+ 
+ * Not all of the persistence actions operate correctly. 
+   
+   ** Delete complains that the entity is detached. 
+   ** After creating a Subscription, it does not appear on the list 
+      until the container is restarted. 
+      
+ * There is no global error handling and logging.
+ 
+ * There is no access security. Anyone can access any user resource.
+ 
+ * Despite the scope of the application, the example could include 
+   a demonstration of handling optimistic locking
+
+CURRENT FOCUS
+
+ * With the application structure in place, work is now focussed on 
+   creating a unit test suit against the data access logic. 
+   
+ * With a unit test suite in place, we may consider using the 
+   "one transaction per request" strategy, if it can be reconciled 
+   with unit testing. 
+
+
+========================================================================
+

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/META-INF/persistence.xml
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/META-INF/persistence.xml?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/META-INF/persistence.xml (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/META-INF/persistence.xml Mon Nov 19 23:37:56 2007
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
+    http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
+    
+	<persistence-unit name="entity"
+		transaction-type="RESOURCE_LOCAL">
+		<provider>
+			oracle.toplink.essentials.PersistenceProvider
+		</provider>
+		<class>entity.protocol.Protocol</class>
+		<class>entity.subscription.Subscription</class>
+		<class>entity.user.User</class>
+		<properties>
+			<property name="toplink.jdbc.driver"
+				value="org.apache.derby.jdbc.ClientDriver" />
+			<property name="toplink.jdbc.url"
+				value="jdbc:derby://localhost:1527/mailreader" />
+			<property name="toplink.jdbc.user" value="app" />
+			<property name="toplink.jdbc.password" value="app" />
+			<property name="toplink.ddl-generation"
+				value="create-tables" />
+		</properties>
+	</persistence-unit>
+
+</persistence>

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/Index.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/Index.java?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/Index.java (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/Index.java Mon Nov 19 23:37:56 2007
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 action;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.struts2.interceptor.SessionAware;
+import com.opensymphony.xwork2.ActionSupport;
+
+/**
+ * <p>
+ * Provides the Struts-specific Business and Persistance Logic API for the
+ * MailReader application.
+ * </p>
+ * <p>
+ * Generic logic should be pushed down to the Entity managers or another facade.
+ * </p>
+ */
+public class Index extends ActionSupport implements SessionAware {
+
+    // ---- STATICS ----
+
+    // General Struts statics
+    public static final String CANCEL = "cancel";
+    public static final String INDEX = "index";
+    public static final String NAMESPACE = "namespace";
+    public static final String NONE = "";
+    public static final String PROFILE_KEY = "ACTION_PROFILE";
+    public static final String REDIRECT_ACTION = "redirect-action";
+    public static final String UPDATE_INPUT = "update!input";
+
+    // MailReader-specific statics
+    public static final String NS_USER = "/user";
+    public static final String NS_SUBSCRIPTION = "/user/subscription";
+    public static final String SUBSCRIPTION_USER_USERNAME = "${subscription.user.username}";
+    public static final String USER = "user";
+    public static final String USER_USERNAME = "${user.username}";
+
+    /**
+     * <p>
+     * Indicate pending "create" operation in a CRUD workflow.
+     * </p>
+     */
+    protected static final String CREATE = "create";
+
+    /**
+     * <p>
+     * Indicate pending "delete" operation in a CRUD workflow.
+     * </p>
+     */
+    protected static final String DELETE = "delete";
+
+    /**
+     * <p>
+     * Indicate pending "update" operation in a CRUD workflow.
+     * </p>
+     */
+    protected static final String UPDATE = "update";
+
+    // ---- PROPERTIES ----
+
+    public String getLocation() {
+        return "/WEB-INF/results/";
+    }
+
+    /**
+     * <p>
+     * Provide a local cache of client properties.
+     * </p>
+     */
+    public Map getProfile() {
+        return (Map) getSession().get(PROFILE_KEY);
+    }
+
+    public void setProfile(Map value) {
+        getSession().put(PROFILE_KEY, value);
+    }
+
+    /**
+     * <p>
+     * Record the CRUD operation in progress.
+     * </p>
+     */
+    private String input = CREATE;
+
+    public String getInput() {
+        return input;
+    }
+
+    public void setInput(String value) {
+        input = value;
+    }
+
+    /**
+     * <p>
+     * Provide the session context, or its proxy.
+     * </p>
+     */
+    private Map session;
+
+    public void setSession(Map value) {
+        session = value;
+        Object profile = getProfile();
+        if (profile == null) {
+            setProfile(new HashMap());
+        }
+    }
+
+    protected Map getSession() {
+        return session;
+    }
+
+    // ---- METHODS ----
+
+    /**
+     * <p>
+     * Signal an early exit from the pending workflow.
+     * </p>
+     */
+    public String cancel() {
+        return CANCEL;
+    }
+
+    /**
+     * <p>
+     * Indicate whether there are no errors.
+     * </p>
+     */
+    public boolean NotErrors() {
+        return !hasErrors();
+    }
+
+}

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package.properties
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package.properties?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package.properties (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package.properties Mon Nov 19 23:37:56 2007
@@ -0,0 +1,106 @@
+hello.message = Congratulations! Struts is up and running ...
+# fields
+user.fullName=Full Name
+user.fromAddress=From Address
+user.password=Password
+user.password1=Password
+user.password2=(Repeat) Password
+user.replyToAddress=Reply To Address
+user.username=Username
+# buttons
+button.cancel=Cancel
+button.confirm=Confirm
+button.reset=Reset
+button.update=Save
+# index
+index.heading=MailReader Options
+index.login=Log into MailReader
+index.login.title=MailReader - Login
+index.register=Register with MailReader
+index.title=MailReader
+index.tour=A Walking Tour of the MailReader Demonstration Application
+# login
+login.title=MailReader - Logon
+# register
+user.title.create=MailReader - Create Registration
+user.index.title=MailReader - Main Menu
+user.index.heading=Menu Options for
+user.index.register=Edit your registration profile
+user.index.logout=Log out of MailReader
+heading.subscriptions=Current Subscriptions
+heading.action=Action
+heading.create=Add
+heading.delete=Delete
+heading.update=Edit
+# subscribe
+subscription.title.create=MailReader - Create Subscription
+subscription.title.delete=MailReader - Delete Subscription
+# option
+option.imap=IMAP Protocol
+option.pop3=POP3 Protocol
+# prompts
+subscription.autoConnect=Auto Connect
+subscription.host=Mail Server
+subscription.password=Mail Password
+subscription.protocol=Server Type
+subscription.username=Mail Username
+
+# error (field)
+error.username.required=Please enter a Username
+error.fullName.required=Please enter a Full Name
+error.fromAddress.required=Please enter a From Address
+error.fromAddress.format=Please correct the email format for From Address
+error.host.required=Please enter a Mail Server host
+error.host.unique=That hostname is already defined
+error.replyToAddress.format=Please correct the email format for Reply To Address
+error.password.required=Please enter a Password
+error.password2.required=Please enter the password again as a confirmation
+error.password.match=Password and confirmation password must match
+error.type.invalid=Server Type must be 'imap' or 'pop3'
+error.type.required=Server Type is required
+# error (global)
+error.database.missing=User database is missing, cannot validate logon credentials
+error.username.unique=That username is already in use - please select another
+error.password.mismatch=Invalid username and/or password, please try again
+linkSubscription.io=I/O Error: {0}
+
+# error messages 
+error.noSubscription=No Subscription bean in user session
+error.password.expired=Your password has expired for username {0}
+error.password.length=Please enter a password that is between 4 and 10 characters in length
+# Standard error messages for validator framework checks
+errors.required=${getText(fieldName)} is required.
+errors.minlength=${getText(fieldName)} cannot be less than {1} characters.
+errors.maxlength=${getText(fieldName)} cannot be greater than {1} characters.
+errors.invalid=${getText(fieldName)} is invalid.
+errors.byte=${getText(fieldName)} must be an byte.
+errors.short=${getText(fieldName)} must be an short.
+errors.integer=${getText(fieldName)} must be an integer.
+errors.long=${getText(fieldName)} must be an long.
+errors.float=${getText(fieldName)} must be an float.
+errors.double=${getText(fieldName)} must be an double.
+errors.date=${getText(fieldName)} is not a date.
+errors.range=${getText(fieldName)} is not in the range ${minLength} through ${maxLength}.
+errors.creditcard=${getText(fieldName)} is not a valid credit card number.
+errors.email=${getText(fieldName)} is an invalid e-mail address.
+errors.literal=${getText(fieldName)}
+
+# struts 1 errors settings
+errors.footer=</ul><hr>
+errors.header=<h3><font color="red">Validation Error</font></h3><p>You must correct the following error(s) before proceeding:</p><ul>
+errors.prefix=<li>
+errors.suffix=</li>
+errors.ioException=I/O exception rendering error messages: {0}
+
+# future use
+change.message=Your password has expired.  Please ask the system administrator to change it.
+change.try=Try Again
+change.title=Password Has Expired
+database.load=Cannot load database from {0}
+struts.messages.invalid.token=Cannot submit this form out of order
+expired.password=User Password has expired for {0}
+linkSubscription.noSubscription=No subscription under attribute {0}
+linkUser.io=I/O Error: {0}
+linkUser.noUser=No user under attribute {0}
+user.title.update=MailReader - Update Registration
+subscription.title.update=MailReader - Update Subscription

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package_ja.properties
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package_ja.properties?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package_ja.properties (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package_ja.properties Mon Nov 19 23:37:56 2007
@@ -0,0 +1,85 @@
+subscription.autoConnect=\u81ea\u52d5\u63a5\u7d9a
+button.cancel=\u30ad\u30e3\u30f3\u30bb\u30eb
+button.confirm=\u78ba\u8a8d
+button.reset=\u30ea\u30bb\u30c3\u30c8
+button.update=\u4fdd\u5b58
+change.message=\u30D1\u30B9\u30EF\u30FC\u30C9\u306E\u6709\u52B9\u671F\u9650\u304C\u904E\u304E\u307E\u3057\u305F\u3002\u30B7\u30B9\u30C6\u30E0\u7BA1\u7406\u8005\u306B\u304A\u554F\u3044\u5408\u308F\u305B\u4E0B\u3055\u3044
+change.title=\u30d1\u30b9\u30ef\u30fc\u30c9\u671f\u9650\u5207\u308c
+change.try=\u518D\u8A66\u884C
+database.load= {0} \u304B\u3089\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u30ED\u30FC\u30C9\u3067\u304D\u307E\u305B\u3093
+error.database.missing=\u30E6\u30FC\u30B6\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u30ED\u30B0\u30AA\u30F3\u306E\u8A8D\u8A3C\u304C\u51FA\u6765\u307E\u305B\u3093
+error.fromAddress.format=From\u30A2\u30C9\u30EC\u30B9\u306E\u66F8\u5F0F\u304C\u6B63\u3057\u304F\u3042\u308A\u307E\u305B\u3093
+error.fromAddress.required=From\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044
+error.fullName.required=\u30D5\u30EB\u30CD\u30FC\u30E0\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044
+error.host.required=\u30E1\u30FC\u30EB\u30B5\u30FC\u30D0\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044
+error.noSubscription=Subscription bean \u304c\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u4e2d\u306b\u3042\u308a\u307e\u305b\u3093
+error.password.expired=\u30E6\u30FC\u30B6 {0} \u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u306E\u6709\u52B9\u671F\u9650\u304C\u904E\u304E\u307E\u3057\u305F
+error.password.match=\u30D1\u30B9\u30EF\u30FC\u30C9\u3068\u78BA\u8A8D\u7528\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u4E00\u81F4\u3057\u3066\u3044\u307E\u305B\u3093
+error.password.mismatch=\u30E6\u30FC\u30B6\u540D\u307E\u305F\u306F\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u4E0D\u6B63\u3067\u3059\u3002\u518D\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044
+error.password.required=\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u5FC5\u8981\u3067\u3059
+error.password2.required=\u30D1\u30B9\u30EF\u30FC\u30C9(\u78BA\u8A8D\u7528)\u304C\u5FC5\u8981\u3067\u3059
+error.replyToAddress.format=\u8FD4\u4FE1\u30A2\u30C9\u30EC\u30B9\u306E\u66F8\u5F0F\u304C\u6B63\u3057\u304F\u3042\u308A\u307E\u305B\u3093
+error.type.invalid=\u30B5\u30FC\u30D0\u30BF\u30A4\u30D7\u306F 'imap' \u304B 'pop3'\u306E\u3069\u3061\u3089\u304B\u3067\u306A\u3051\u308C\u3070\u306A\u308A\u307E\u305B\u3093
+error.type.required=\u30B5\u30FC\u30D0\u30BF\u30A4\u30D7\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044
+error.username.required=\u30E6\u30FC\u30B6\u540D\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044
+error.username.unique=\u305D\u306E\u30E6\u30FC\u30B6\u540D\u306F\u65E2\u306B\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059\u3002 \u5225\u306E\u30E6\u30FC\u30B6\u540D\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044
+errors.byte=${getText(fieldName)} \u306fbyte\u578b\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002
+errors.creditcard=${getText(fieldName)} \u306f\u6b63\u3057\u3044\u30af\u30ec\u30b8\u30c3\u30c8\u30ab\u30fc\u30c9\u756a\u53f7\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002
+errors.date=${getText(fieldName)} \u306f\u65e5\u4ed8\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002
+errors.double=${getText(fieldName)} \u306fdouble\u578b\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002
+errors.email=${getText(fieldName)} \u306f\u6b63\u3057\u3044\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002
+errors.float=${getText(fieldName)} \u306ffloat\u578b\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002
+errors.footer=</ul><hr>
+errors.header=<h3><font color="red">\u5165\u529b\u30c1\u30a7\u30c3\u30af\u30a8\u30e9\u30fc</font></h3><p>\u4ee5\u4e0b\u306e\u30a8\u30e9\u30fc\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044:</p><ul>
+errors.integer=${getText(fieldName)} \u306finteger\u578b\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002
+errors.invalid=${getText(fieldName)} \u306f\u6b63\u3057\u304f\u3042\u308a\u307e\u305b\u3093\u3002
+errors.ioException=I/O\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
+errors.long=${getText(fieldName)} \u306flong\u578b\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002
+errors.maxlength=${getText(fieldName)} \u306f {2} \u6587\u5b57\u4ee5\u4e0b\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002
+errors.minlength=${getText(fieldName)} \u306f {1} \u6587\u5b57\u4ee5\u4e0a\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002
+errors.prefix=<li>
+errors.range=${getText(fieldName)} \u306f {1} \u304b\u3089 {2} \u306e\u9593\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002
+errors.required=${getText(fieldName)} \u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+errors.short=${getText(fieldName)} \u306fshort\u578b\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002
+errors.suffix=</li>
+expired.password=\u30E6\u30FC\u30B6 {0} \u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u306E\u6709\u52B9\u671F\u9650\u304C\u904E\u304E\u307E\u3057\u305F
+user.fromAddress=From\u30a2\u30c9\u30ec\u30b9
+user.fullName=\u30d5\u30eb\u30cd\u30fc\u30e0
+heading.action=\u64cd\u4f5c
+subscriptions.heading=\u73fe\u5728\u306e\u8cfc\u8aad\u60c5\u5831
+index.register=MailReader\u30c7\u30e2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 - \u30e6\u30fc\u30b6\u767b\u9332
+linkSubscription.io=I/O\u30a8\u30e9\u30fc: {0}
+linkSubscription.noSubscription=\u5c5e\u6027 {0} \u306b\u8cfc\u8aad\u60c5\u5831\u304c\u5b58\u5728\u3057\u307e\u305b\u3093
+linkUser.io=I/O\u30a8\u30e9\u30fc: {0}
+linkUser.noUser=\u5c5e\u6027 {0} \u306b\u30e6\u30fc\u30b6\u60c5\u5831\u304c\u5b58\u5728\u3057\u307e\u305b\u3093
+login.title=MailReader\u30c7\u30e2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 - \u30ed\u30b0\u30aa\u30f3
+subscription.Hostname=\u30e1\u30fc\u30eb\u30b5\u30fc\u30d0
+subscription.password=\u30e1\u30fc\u30eb\u30d1\u30b9\u30ef\u30fc\u30c9
+subscription.protocol=\u30b5\u30fc\u30d0\u30bf\u30a4\u30d7
+subscription.username=\u30e1\u30fc\u30eb\u30e6\u30fc\u30b6\u540d
+user.index.heading=\u30e1\u30a4\u30f3\u30e1\u30cb\u30e5\u30fc
+user.index.logout=MailReader \u30c7\u30e2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30ed\u30b0\u30aa\u30d5
+user.index.register=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u306e\u7de8\u96c6
+user.index.title=MailReader\u30c7\u30e2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 - \u30e1\u30a4\u30f3\u30e1\u30cb\u30e5\u30fc
+option.imap=IMAP \u30d7\u30ed\u30c8\u30b3\u30eb
+option.pop3=POP3 \u30d7\u30ed\u30c8\u30b3\u30eb
+user.password=\u30d1\u30b9\u30ef\u30fc\u30c9
+user.password1=\u30d1\u30b9\u30ef\u30fc\u30c9
+user.password2=\u30d1\u30b9\u30ef\u30fc\u30c9(\u78ba\u8a8d\u7528)
+heading.delete=\u524a\u9664
+heading.create=\u65b0\u898f\u4f5c\u6210
+heading.update=\u7de8\u96c6
+user.title.create=MailReader\u30c7\u30e2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 - \u30e6\u30fc\u30b6\u767b\u9332
+user.title.update=MailReader\u30c7\u30e2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 - \u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u7de8\u96c6
+user.replyToAddress=\u8fd4\u4fe1\u30a2\u30c9\u30ec\u30b9
+struts.messages.invalid.token=\u3053\u306E\u30D5\u30A9\u30FC\u30E0\u306E\u5185\u5BB9\u304C\u6B63\u3057\u304F\u306A\u3044\u305F\u3081\u9001\u4FE1\u3059\u308B\u3053\u3068\u304C\u51FA\u6765\u307E\u305B\u3093
+subscription.title.create=\u30e1\u30fc\u30eb\u8cfc\u8aad\u60c5\u5831\u306e\u65b0\u898f\u4f5c\u6210
+subscription.title.delete=\u30e1\u30fc\u30eb\u8cfc\u8aad\u60c5\u5831\u306e\u524a\u9664
+subscription.title.update=\u30e1\u30fc\u30eb\u8cfc\u8aad\u60c5\u5831\u306e\u7de8\u96c6
+user.username=\u30e6\u30fc\u30b6\u540d
+index.heading=MailReader\u30c7\u30e2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 \u30aa\u30d7\u30b7\u30e7\u30f3
+index.login=MailReader\u30c7\u30e2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 - \u30ed\u30b0\u30aa\u30f3
+index.login.title=MailReader\u30c7\u30e2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 - \u30ed\u30b0\u30aa\u30f3
+index.registration=MailReader\u30c7\u30e2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3 - \u30e6\u30fc\u30b6\u767b\u9332
+index.title=MailReader\u30c7\u30e2\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3(Struts 1.1-dev)
+index.tour=\u30b5\u30f3\u30d7\u30eb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u6563\u7b56\u3059\u308b

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package_ru.properties
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package_ru.properties?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package_ru.properties (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/package_ru.properties Mon Nov 19 23:37:56 2007
@@ -0,0 +1,84 @@
+subscription.autoConnect=\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435:
+button.cancel=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c
+button.confirm=\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c
+button.reset=\u0421\u0431\u0440\u043e\u0441\u0438\u0442\u044c
+button.update=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c
+change.message=Your password has expired.  Please ask the system administrator to change it.
+change.title=Password Has Expired
+change.try=Try Again
+database.load=\u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430 \u0438\u0437 {0}
+error.database.missing=\u041d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f - \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0441\u0442\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e.
+error.fromAddress.format=\u0412 \u043f\u043e\u043b\u0435 '\u0410\u0434\u0440\u0435\u0441 \u041e\u0442:' \u0443\u043a\u0430\u0437\u0430\u043d \u0430\u0434\u0440\u0435\u0441 \u0432 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435.
+error.fromAddress.required=\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 \u0432 \u043f\u043e\u043b\u0435 '\u0410\u0434\u0440\u0435\u0441 \u041e\u0442:'.
+error.fullName.required=\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0435 \u0438\u043c\u044f.
+error.host.required=\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440.
+error.noSubscription=\u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u0432 \u0441\u0435\u0441\u0441\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f
+error.password.expired=Your password has expired for username {0}
+error.password.match=\u041f\u0430\u0440\u043e\u043b\u044c \u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u043e\u043b\u044f \u043d\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u044e\u0442.
+error.password.mismatch=\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u0438\u043c\u044f \u0438/\u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c - \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0441\u043d\u043e\u0432\u0430.
+error.password.required=\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c.
+error.password2.required=\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u043e\u043b\u044f.
+error.replyToAddress.format=\u0412 \u043f\u043e\u043b\u0435 '\u0410\u0434\u0440\u0435\u0441 \u041e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043d\u0430:' \u0443\u043a\u0430\u0437\u0430\u043d \u0430\u0434\u0440\u0435\u0441 \u0432 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435.
+error.type.invalid=\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0442\u0438\u043f\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u043d\u044b \u043b\u0438\u0448\u044c 'imap' \u0438\u043b\u0438 'pop3'
+error.type.required=\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0442\u0438\u043f \u0441\u0435\u0440\u0432\u0435\u0440\u0430
+error.username.required=\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f
+error.username.unique=\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f - \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0434\u0440\u0443\u0433\u043e\u0435 \u0438\u043c\u044f.
+errors.byte=${getText(fieldName)} must be an byte.
+errors.creditcard=${getText(fieldName)} is not a valid credit card number.
+errors.date=${getText(fieldName)} is not a date.
+errors.double=${getText(fieldName)} must be an double.
+errors.email=${getText(fieldName)} is an invalid e-mail address.
+errors.float=${getText(fieldName)} must be an float.
+errors.footer=</ul><hr>
+errors.header=<h3><font color="red">\u041e\u0448\u0438\u0431\u043a\u0438 \u043f\u0440\u0438 \u0437\u0430\u043d\u0435\u0441\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445</font></h3><p>\u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u043d\u0438\u0436\u0435 \u043e\u0448\u0438\u0431\u043a\u0438:</p><ul>
+errors.integer=${getText(fieldName)} must be an integer.
+errors.invalid=${getText(fieldName)} is invalid.
+errors.ioException=\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0432\u043e\u0434\u0430/\u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445: {0}
+errors.long=${getText(fieldName)} must be an long.
+errors.maxlength=${getText(fieldName)} cannot be greater than {2} characters.
+errors.minlength=${getText(fieldName)} cannot be less than {1} characters.
+errors.prefix=<li>
+errors.range=${getText(fieldName)} is not in the range {1} through {2}.
+errors.required=${getText(fieldName)} is required.
+errors.short=${getText(fieldName)} must be an short.
+errors.suffix=</li>
+expired.password=User Password has expired for {0}
+user.fromAddress=\u0410\u0434\u0440\u0435\u0441 \u041e\u0442:
+user.fullName=\u041f\u043e\u043b\u043d\u043e\u0435 \u0438\u043c\u044f:
+heading.action=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435
+subscriptions.heading=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438
+linkSubscription.io=\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0432\u043e\u0434\u0430/\u0432\u044b\u0432\u043e\u0434\u0430 (\u0434\u043b\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438): {0}
+linkSubscription.noSubscription=\u0410\u0442\u0440\u0438\u0431\u0443\u0442 {0} \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0435 \u0438\u043b\u0438 \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442.
+linkUser.io=\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0432\u043e\u0434\u0430/\u0432\u044b\u0432\u043e\u0434\u0430 (\u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f): {0}
+linkUser.noUser=\u0410\u0442\u0440\u0438\u0431\u0443\u0442 {0} \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435 \u0438\u043b\u0438 \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442.
+login.title=\u0414\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0427\u0442\u0435\u043d\u0438\u0435 \u043f\u043e\u0447\u0442\u044b - \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0438\u043c\u0435\u043d\u0438 \u0438 \u043f\u0430\u0440\u043e\u043b\u044f.
+subscription.Hostname=\u041f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440:
+subscription.password=\u041f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u043f\u043e\u0447\u0442\u043e\u0432\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430:
+subscription.protocol=\u0422\u0438\u043f \u0441\u0435\u0440\u0432\u0435\u0440\u0430:
+subscription.username=\u0418\u043c\u044f \u0434\u043b\u044f \u043f\u043e\u0447\u0442\u043e\u0432\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430:
+mainuser.index.title=\u0414\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 '\u0427\u0442\u0435\u043d\u0438\u0435 \u043f\u043e\u0447\u0442\u044b' - \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e
+user.index.heading=\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0433\u043b\u0430\u0432\u043d\u043e\u0433\u043e \u043c\u0435\u043d\u044e \u0434\u043b\u044f
+user.index.register=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0432\u043e\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438
+user.index.logout=\u0412\u044b\u0439\u0442\u0438
+option.imap=\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b IMAP
+option.pop3=\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b POP3
+user.password=\u041f\u0430\u0440\u043e\u043b\u044c:
+user.password1=\u041f\u0430\u0440\u043e\u043b\u044c:
+user.password2=(\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435) \u041f\u0430\u0440\u043e\u043b\u044c:
+heading.delete=\u0423\u0434\u0430\u043b\u0438\u0442\u044c
+heading.create=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c
+heading.update=\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c
+user.title.create=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f
+user.title.update=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u0441\u0432\u043e\u0435\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438
+replyToAddress=\u0410\u0434\u0440\u0435\u0441 \u041e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043d\u0430:
+struts.messages.invalid.token=\u042d\u0442\u0430 \u0444\u043e\u0440\u043c\u0430 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u0430 - \u043d\u0430\u0440\u0443\u0448\u0435\u043d\u0438\u0435 \u043f\u043e\u0440\u044f\u0434\u043a\u0430 \u0437\u0430\u043d\u0435\u0441\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445.
+subscription.title.create=\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0443
+subscription.title.delete=\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0443
+subscription.title.update=\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0443
+user.username=\u0418\u043c\u044f:
+index.heading=\u0414\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 '\u0427\u0442\u0435\u043d\u0438\u0435 \u043f\u043e\u0447\u0442\u044b'
+index.login=\u0412\u043e\u0439\u0442\u0438 \u043a\u0430\u043a \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c.registration=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f
+index.login.title=\u0412\u043e\u0439\u0442\u0438 \u043a\u0430\u043a \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c.registration=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f
+index.register==\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f
+index.title=\u0414\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 '\u0427\u0442\u0435\u043d\u0438\u0435 \u043f\u043e\u0447\u0442\u044b' (Struts 1.1-dev)
+index.tour=\u041e\u0431\u0437\u043e\u0440 \u0414\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f '\u0427\u0442\u0435\u043d\u0438\u0435 \u043f\u043e\u0447\u0442\u044b'

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Create.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Create.java?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Create.java (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Create.java Mon Nov 19 23:37:56 2007
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 action.user;
+
+import org.apache.struts2.config.Result;
+import org.apache.struts2.config.Results;
+import org.apache.struts2.dispatcher.ServletActionRedirectResult;
+import com.opensymphony.xwork2.validator.annotations.EmailValidator;
+import com.opensymphony.xwork2.validator.annotations.ExpressionValidator;
+import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
+import com.opensymphony.xwork2.validator.annotations.StringLengthFieldValidator;
+import com.opensymphony.xwork2.validator.annotations.Validation;
+import com.opensymphony.xwork2.validator.annotations.Validations;
+
+@Results( {
+        @Result(name = Index.CANCEL, value = Index.INDEX, type = ServletActionRedirectResult.class, params = {
+                Index.NAMESPACE, Index.NONE }),
+        @Result(name = Index.SUCCESS, value = Index.INDEX, type = ServletActionRedirectResult.class, params = {
+                Index.USER, Index.USER_USERNAME }) })
+@Validation()
+public class Create extends Index {
+
+    // FIXME: Can the email validations by applied to getUser?
+    @Validations(requiredStrings = {
+            @RequiredStringValidator(fieldName = "user.username", key = "error.username.required", message = ""),
+            @RequiredStringValidator(fieldName = "user.fullName", key = "error.fullName.required", message = ""),
+            @RequiredStringValidator(fieldName = "user.fromAddress", key = "error.fromAddress.required", message = ""),
+            @RequiredStringValidator(fieldName = "user.password1", key = "error.password.required", message = ""),
+            @RequiredStringValidator(fieldName = "user.password2", key = "error.password2.required", message = "") }, stringLengthFields = { @StringLengthFieldValidator(fieldName = "user.password1", key = "error.password.length", message = "", trim = true, minLength = "4", maxLength = "12") }, emails = {
+            @EmailValidator(fieldName = "user.fromAddress", key = Index.ERROR_FROM_ADDRESS_FORMAT, message = Index.NONE),
+            @EmailValidator(fieldName = "user.replyTo", key = Index.ERROR_REPLY_TO_ADDRESS_FORMAT, message = Index.NONE) })
+    @StringLengthFieldValidator(fieldName = "user.password1", key = "error.password.length", message = "", trim = true, minLength = "4", maxLength = "12")
+    @ExpressionValidator(key = "error.password.match", message = "", expression = "password1 eq password2")
+    public String execute() throws Exception {
+        if (!CREATE.equals(getInput()))
+            return INPUT;
+        create();
+        return (hasErrors()) ? INPUT : SUCCESS;
+    }
+
+    public String input() throws Exception {
+        setInput(CREATE);
+        return super.input();
+    }
+}

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Index.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Index.java?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Index.java (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Index.java Mon Nov 19 23:37:56 2007
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 action.user;
+
+import entity.user.User;
+import entity.user.UserManager;
+import entity.user.UserManagerInterface;
+
+public class Index extends action.Index {
+
+    // ---- STATICS ----
+
+    static final String ERROR_FROM_ADDRESS_FORMAT = "error.fromAddress.format";
+
+    static final String ERROR_PASSWORD_MATCH = "error.password.match";
+
+    static final String ERROR_PASSWORD_LENGTH = "error.password.length";
+
+    static final String ERROR_REPLY_TO_ADDRESS_FORMAT = "error.replyToAddress.format";
+
+    static final String ERROR_CREDENTIALS_MISMATCH = "error.password.mismatch";
+
+    static final String ERROR_USERNAME_UNIQUE = "error.username.unique";
+
+    static final String PASSWORD_MISMATCH_FIELD = "password";
+
+    // ---- PROPERTIES ----
+
+    UserManagerInterface manager;
+
+    public String getLocation() {
+        return "/WEB-INF/results/user/";
+    }
+
+    private User user;
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User value) {
+        user = value;
+    }
+
+    // ---- METHODS ----
+
+    protected void authenticate() throws Exception {
+        String username = getUser().getUsername();
+        String password = getUser().getPassword1();
+
+        User foundValue = manager.findByName(username);
+        if ((foundValue != null) && !foundValue.getPassword().equals(password)) {
+            foundValue = null;
+        }
+
+        boolean isFound = (foundValue != null);
+        if (isFound) {
+            setUser(foundValue);
+            login();
+        } else {
+            addFieldError(PASSWORD_MISMATCH_FIELD,
+                    getText(ERROR_CREDENTIALS_MISMATCH));
+        }
+    }
+
+    protected void create() throws Exception {
+        String username = getUser().getUsername();
+        String password = getUser().getPassword1();
+
+        User foundValue = manager.findByName(username);
+        boolean isNameInUse = (foundValue != null);
+        if (isNameInUse) {
+            addActionError(getText(ERROR_USERNAME_UNIQUE));
+        } else {
+            User newValue = getUser();
+            newValue.setPassword(password);
+            // Let other details carryover
+            manager.create(newValue);
+            login();
+        }
+    }
+
+    protected void login() {
+        getProfile().put(USER, getUser());
+    }
+
+    protected void logout() {
+        getProfile().put(USER, null);
+    }
+
+    protected void update() throws Exception {
+        User result = getUser();
+        if (manager.hasId(result)) {
+            manager.update(result);
+        } else {
+            addActionError(getText(ERROR_CREDENTIALS_MISMATCH));
+        }
+    }
+
+    /**
+     * <p>
+     * Instantiate default instance.
+     * </p>
+     */
+    public Index() {
+        manager = new UserManager();
+    }
+
+    /**
+     * <p>
+     * Instantiate instance using specified <code>IUserManager</code>.
+     * </p>
+     * 
+     * @param manager
+     *            IUserManager instance
+     */
+    public Index(UserManagerInterface manager) {
+        this.manager = manager;
+    }
+
+}

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Login.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Login.java?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Login.java (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Login.java Mon Nov 19 23:37:56 2007
@@ -0,0 +1,27 @@
+package action.user;
+
+import org.apache.struts2.config.Result;
+import org.apache.struts2.config.Results;
+import org.apache.struts2.dispatcher.ServletActionRedirectResult;
+
+import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
+import com.opensymphony.xwork2.validator.annotations.Validation;
+import com.opensymphony.xwork2.validator.annotations.Validations;
+
+@Results( {
+        @Result(name = Index.CANCEL, value = Index.INDEX, type = ServletActionRedirectResult.class, params = {
+                Index.NAMESPACE, Index.NONE }),
+        @Result(name = Index.SUCCESS, value = Index.INDEX, type = ServletActionRedirectResult.class, params = {
+                Index.USER, Index.USER_USERNAME }) })
+@Validation()
+public class Login extends Index {
+
+    @Validations(requiredStrings = {
+            @RequiredStringValidator(fieldName = "user.username", key = "error.username.required", message = ""),
+            @RequiredStringValidator(fieldName = "user.password1", key = "error.password.required", message = "") })
+    public String execute() throws Exception {
+        authenticate();
+        return (hasErrors()) ? INPUT : SUCCESS;
+    }
+
+}

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Logout.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Logout.java?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Logout.java (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Logout.java Mon Nov 19 23:37:56 2007
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 action.user;
+
+import org.apache.struts2.config.Result;
+import org.apache.struts2.dispatcher.ServletActionRedirectResult;
+
+@Result(name = Index.SUCCESS, value = Index.INDEX, type = ServletActionRedirectResult.class, params = {
+        Index.NAMESPACE, Index.NONE })
+public class Logout extends Index {
+
+    public String execute() {
+        logout();
+        return SUCCESS;
+    }
+}

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Update.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Update.java?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Update.java (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/Update.java Mon Nov 19 23:37:56 2007
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 action.user;
+
+import org.apache.struts2.config.Result;
+import org.apache.struts2.config.Results;
+import org.apache.struts2.dispatcher.ServletActionRedirectResult;
+
+import com.opensymphony.xwork2.validator.annotations.EmailValidator;
+import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
+import com.opensymphony.xwork2.validator.annotations.Validation;
+import com.opensymphony.xwork2.validator.annotations.Validations;
+
+@Results( {
+        @Result(name = Index.CANCEL, value = Index.INDEX, type = ServletActionRedirectResult.class, params = {
+                Index.USER, Index.USER_USERNAME }),
+        @Result(name = Index.SUCCESS, value = Index.INDEX, type = ServletActionRedirectResult.class, params = {
+                Index.USER, Index.USER_USERNAME }) })
+@Validation()
+public class Update extends Index {
+
+    @Validations(requiredStrings = {
+            @RequiredStringValidator(fieldName = "user.username", key = "error.username.required", message = "", shortCircuit = true),
+            @RequiredStringValidator(fieldName = "user.fullName", key = "error.fullname.required", message = "", shortCircuit = true),
+            @RequiredStringValidator(fieldName = "user.fromAddress", key = "error.fromaddress.required", message = "", shortCircuit = true) }, emails = {
+            @EmailValidator(fieldName = "user.fromAddress", key = Index.ERROR_REPLY_TO_ADDRESS_FORMAT, message = Index.NONE),
+            @EmailValidator(fieldName = "user.replyTo", key = Index.ERROR_FROM_ADDRESS_FORMAT, message = Index.NONE) })
+    public String execute() throws Exception {
+
+        if (!UPDATE.equals(getInput()))
+            return INPUT;
+
+        String newPassword = getUser().getPassword1();
+        boolean changing = ((null != newPassword) && (newPassword.length() > 0));
+        if (changing) {
+            String confirmPassword = getUser().getPassword2();
+            boolean matches = ((null != confirmPassword) && (confirmPassword
+                    .equals(newPassword)));
+            if (matches) {
+                getUser().setPassword(newPassword);
+            } else {
+                addActionError(getText(ERROR_PASSWORD_MATCH));
+                return INPUT;
+            }
+        }
+
+        update();
+        return (hasErrors()) ? INPUT : SUCCESS;
+    }
+
+    public String input() throws Exception {
+        setInput(UPDATE);
+        return super.input();
+    }
+}

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Create.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Create.java?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Create.java (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Create.java Mon Nov 19 23:37:56 2007
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 action.user.subscription;
+
+import org.apache.struts2.config.Result;
+import org.apache.struts2.dispatcher.ServletActionRedirectResult;
+
+@Result(name = Index.CANCEL, value = Index.UPDATE_INPUT, type = ServletActionRedirectResult.class, params = {
+        Index.NAMESPACE, Index.NS_USER, Index.USER, Index.USER_USERNAME })
+public class Create extends Index {
+
+    public String input() throws Exception {
+        setInput(CREATE);
+        return super.input();
+    }
+
+}

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Delete.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Delete.java?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Delete.java (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Delete.java Mon Nov 19 23:37:56 2007
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 action.user.subscription;
+
+public class Delete extends Index {
+
+    public String input() throws Exception {
+        setInput(DELETE);
+        return manager.hasId(getSubscription()) ? super.input() : ERROR;
+    }
+
+}

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Index.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Index.java?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Index.java (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Index.java Mon Nov 19 23:37:56 2007
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 action.user.subscription;
+
+import java.util.Map;
+
+import org.apache.struts2.config.Result;
+import org.apache.struts2.config.Results;
+import org.apache.struts2.dispatcher.ServletActionRedirectResult;
+
+import com.opensymphony.xwork2.Preparable;
+import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
+import com.opensymphony.xwork2.validator.annotations.RequiredFieldValidator;
+import com.opensymphony.xwork2.validator.annotations.Validation;
+import com.opensymphony.xwork2.validator.annotations.Validations;
+
+import entity.protocol.ProtocolManager;
+import entity.protocol.ProtocolManagerInterface;
+import entity.subscription.Subscription;
+import entity.subscription.SubscriptionManager;
+import entity.subscription.SubscriptionManagerInterface;
+import entity.user.User;
+
+@Results( {
+        @Result(name = Index.CANCEL, value = Index.UPDATE_INPUT, type = ServletActionRedirectResult.class, params = {
+                Index.NAMESPACE, Index.NS_USER, Index.USER,
+                Index.SUBSCRIPTION_USER_USERNAME }),
+        @Result(name = Index.ERROR, value = Index.UPDATE_INPUT, type = ServletActionRedirectResult.class, params = {
+                Index.NAMESPACE, Index.NS_USER, Index.USER,
+                Index.SUBSCRIPTION_USER_USERNAME }),
+        @Result(name = Index.SUCCESS, value = Index.UPDATE_INPUT, type = ServletActionRedirectResult.class, params = {
+                Index.NAMESPACE, Index.NS_USER, Index.USER,
+                Index.SUBSCRIPTION_USER_USERNAME }) })
+@Validation()
+public class Index extends action.user.Index implements Preparable {
+
+    protected static final String ERROR_HOST_UNIQUE = "error.host.unique";
+
+    static final String ERROR_CREDENTIALS_MISMATCH = "error.password.mismatch";
+
+    protected SubscriptionManagerInterface manager;
+    private ProtocolManagerInterface protocolManager;
+
+    private Map protocols;
+
+    public Map getProtocols() {
+        return protocols;
+    }
+
+    public void setProtocols(Map value) {
+        protocols = value;
+    }
+
+    private Subscription subscription;
+
+    public Subscription getSubscription() {
+        return subscription;
+    }
+
+    public void setSubscription(Subscription value) {
+        subscription = value;
+    }
+
+    private boolean validateHost(Subscription value) {
+        String name = value.getHost();
+        Subscription foundValue = manager.findByName(name);
+        boolean isNameInUse = (foundValue != null);
+        if (isNameInUse)
+            addFieldError(Subscription.HOST, getText(ERROR_HOST_UNIQUE));
+        return NotErrors();
+    }
+
+    private boolean validateUser(Subscription value) {
+        boolean needUser = (value.getUser() == null);
+        if (needUser) {
+            User defaultUser = getUser();
+            if (defaultUser == null) {
+                addFieldError(Subscription.HOST,
+                        getText(ERROR_CREDENTIALS_MISMATCH));
+            } else {
+                value.setUser(getUser());
+            }
+        }
+        return NotErrors();
+    }
+
+    protected void create() throws Exception {
+        Subscription value = getSubscription();
+        boolean isValid = ((validateHost(value)) && (validateUser(value)));
+        if (isValid)
+            manager.create(value);
+    }
+
+    protected void delete() throws Exception {
+        Subscription value = getSubscription();
+        User user = value.getUser();
+        user.removeSubscription(value);
+        manager.delete(value);
+    }
+
+    protected void update() throws Exception {
+        Subscription result = getSubscription();
+        if (manager.hasId(result)) {
+            manager.update(result);
+        } else {
+            addActionError(getText(ERROR_CREDENTIALS_MISMATCH));
+        }
+    }
+
+    @Validations(requiredStrings = {
+            @RequiredStringValidator(fieldName = "subscription.host", key = "error.host.required", message = "", shortCircuit = true),
+            @RequiredStringValidator(fieldName = "subscription.username", key = "error.username.required", message = "", shortCircuit = true),
+            @RequiredStringValidator(fieldName = "subscription.password", key = "error.password.required", message = "", shortCircuit = true) })
+    @RequiredFieldValidator(fieldName = "subscription.protocol", key = "error.type.required", message = "", shortCircuit = true)
+    public String execute() throws Exception {
+
+        String input = getInput();
+
+        if (CREATE.equals(input))
+            create();
+
+        if (UPDATE.equals(input))
+            update();
+
+        if (DELETE.equals(input))
+            delete();
+
+        if (hasErrors())
+            return INPUT;
+
+        return SUCCESS;
+
+    }
+
+    public void prepare() {
+        setProtocols(protocolManager.findAllAsMap());
+    }
+
+    /**
+     * <p>
+     * Instantiate default instance.
+     * </p>
+     */
+    public Index() {
+        manager = new SubscriptionManager();
+        protocolManager = new ProtocolManager();
+    }
+
+    /**
+     * <p>
+     * Instantiate instance using specified <code>ISubscriptionManager</code>
+     * and <code>IProtocolManager</code> .
+     * </p>
+     * 
+     * @param manager
+     *            IUserManager instance
+     */
+    public Index(SubscriptionManagerInterface manager,
+            ProtocolManagerInterface protocolManager) {
+        this.manager = manager;
+        this.protocolManager = protocolManager;
+    }
+
+}

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Update.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Update.java?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Update.java (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/action/user/subscription/Update.java Mon Nov 19 23:37:56 2007
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 action.user.subscription;
+
+public class Update extends Index {
+
+    public String input() throws Exception {
+        setInput(UPDATE);
+        return manager.hasId(getSubscription()) ? super.input() : ERROR;
+    }
+
+}

Added: struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/EntityManagerHelper.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/EntityManagerHelper.java?rev=596559&view=auto
==============================================================================
--- struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/EntityManagerHelper.java (added)
+++ struts/sandbox/trunk/jpa-mailreader/src/main/java/entity/EntityManagerHelper.java Mon Nov 19 23:37:56 2007
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 entity;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import javax.persistence.Query;
+
+/**
+ * <p>
+ * Provide access to JPA implementation using static methods.
+ * </p>
+ */
+public class EntityManagerHelper {
+
+    static final String PERSISTENCE_UNIT = "entity";
+
+    private static final EntityManagerFactory emf;
+    private static final ThreadLocal<EntityManager> threadLocal;
+    private static final Logger logger;
+
+    static {
+        emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);
+        threadLocal = new ThreadLocal<EntityManager>();
+        logger = Logger.getLogger(PERSISTENCE_UNIT);
+        logger.setLevel(Level.ALL);
+    }
+
+    public static EntityManager getEntityManager() {
+        EntityManager manager = threadLocal.get();
+        if (manager == null || !manager.isOpen()) {
+            manager = emf.createEntityManager();
+            threadLocal.set(manager);
+        }
+        return manager;
+    }
+
+    public static void closeEntityManager() {
+        EntityManager em = threadLocal.get();
+        threadLocal.set(null);
+        if (em != null)
+            em.close();
+    }
+
+    public static void beginTransaction() {
+        getEntityManager().getTransaction().begin();
+    }
+
+    public static void commit() {
+        getEntityManager().getTransaction().commit();
+    }
+
+    public static void rollback() {
+        getEntityManager().getTransaction().rollback();
+    }
+
+    public static Query createQuery(String query) {
+        return getEntityManager().createQuery(query);
+    }
+
+    public static void log(String info, Level level, Throwable ex) {
+        logger.log(level, info, ex);
+    }
+
+    public static void log(String info, Throwable ex) {
+        logger.log(Level.INFO, info, ex);
+    }
+
+}