You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rd...@apache.org on 2008/11/12 20:34:18 UTC

svn commit: r713471 - in /james/server/trunk: ./ jpa-store/ jpa-store/src/main/java/org/apache/james/server/jpa/ jpa-store/src/test/java/org/ jpa-store/src/test/java/org/apache/ jpa-store/src/test/java/org/apache/james/ jpa-store/src/test/java/org/apac...

Author: rdonkin
Date: Wed Nov 12 11:34:18 2008
New Revision: 713471

URL: http://svn.apache.org/viewvc?rev=713471&view=rev
Log:
Basic proof of concept implementation. More work needed.

Added:
    james/server/trunk/jpa-store/src/test/java/org/
    james/server/trunk/jpa-store/src/test/java/org/apache/
    james/server/trunk/jpa-store/src/test/java/org/apache/james/
    james/server/trunk/jpa-store/src/test/java/org/apache/james/server/
    james/server/trunk/jpa-store/src/test/java/org/apache/james/server/jpa/
    james/server/trunk/jpa-store/src/test/java/org/apache/james/server/jpa/JpaUsersRepositoryTest.java
Modified:
    james/server/trunk/include.properties
    james/server/trunk/jpa-store/build.xml
    james/server/trunk/jpa-store/src/main/java/org/apache/james/server/jpa/JPAUser.java
    james/server/trunk/jpa-store/src/main/java/org/apache/james/server/jpa/JPAUsersRepository.java

Modified: james/server/trunk/include.properties
URL: http://svn.apache.org/viewvc/james/server/trunk/include.properties?rev=713471&r1=713470&r2=713471&view=diff
==============================================================================
--- james/server/trunk/include.properties (original)
+++ james/server/trunk/include.properties Wed Nov 12 11:34:18 2008
@@ -330,8 +330,8 @@
 
 
 # ------ JPA --------------------------
-jta-spec.jar=${path.lib.geronimo.specs}/geronimo-jta_1.1_spec-1.1
-jpa-spec.jar=${path.lib.geronimo.specs}/geronimo-jpa_3.0_spec-1.0
+jta-spec.jar=${path.lib.geronimo.specs}/geronimo-jta_1.1_spec-1.1.jar
+jpa-spec.jar=${path.lib.geronimo.specs}/geronimo-jpa_3.0_spec-1.0.jar
 
 # ------ OpenJPA ----------------------
 serp.jar=${path.lib.serp}/serp-1.13.1.jar

Modified: james/server/trunk/jpa-store/build.xml
URL: http://svn.apache.org/viewvc/james/server/trunk/jpa-store/build.xml?rev=713471&r1=713470&r2=713471&view=diff
==============================================================================
--- james/server/trunk/jpa-store/build.xml (original)
+++ james/server/trunk/jpa-store/build.xml Wed Nov 12 11:34:18 2008
@@ -1,6 +1,57 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.    
+-->
 <project default="main" name="jpa-store">
   <description>Builds jpa-function module. This is an function module.</description>
   <property name="name.module" value="jpa-store" />
   <import file="../build-tools/function-build.xml" optional="no" />
+ 
+    <target 
+        name='build'
+        description='Builds without cleaning'
+        depends='check-environment, conditional-clean, set-classpath'
+        unless='dont.build.module'>
+          <echo>Building ${name.module}</echo>   
+          <CompileMainSource/>   
+          <path id='path.enhance.jpa'>
+            <path refid='classpath.base'/>
+            <path refid='classpath.openjpa.repo'/>   
+          </path>
+            
+          <taskdef 
+                name="openjpac" 
+                classname="org.apache.openjpa.ant.PCEnhancerTask"
+                classpathref="path.enhance.jpa"/>
+         
+          <openjpac>
+            <fileset dir="${dir.src.java}">
+              <include name="org/apache/james/jpa/**/*.java" />
+            </fileset>
+            <classpath>
+                <path refid='classpath.main'/>
+                <path refid='classpath.openjpa.repo'/>   
+                <pathelement location="${dir.src.java}"/>   
+                <pathelement location="${dir.build.bin}"/>
+            </classpath>   
+            <config
+               log="TOOL=TRACE"   
+               metaDataFactory="jpa(Types=org.apache.james.server.jpa.JPAUser)"/>   
+          </openjpac>
+    </target>  
 </project>

Modified: james/server/trunk/jpa-store/src/main/java/org/apache/james/server/jpa/JPAUser.java
URL: http://svn.apache.org/viewvc/james/server/trunk/jpa-store/src/main/java/org/apache/james/server/jpa/JPAUser.java?rev=713471&r1=713470&r2=713471&view=diff
==============================================================================
--- james/server/trunk/jpa-store/src/main/java/org/apache/james/server/jpa/JPAUser.java (original)
+++ james/server/trunk/jpa-store/src/main/java/org/apache/james/server/jpa/JPAUser.java Wed Nov 12 11:34:18 2008
@@ -19,22 +19,89 @@
 
 package org.apache.james.server.jpa;
 
+import javax.persistence.Basic;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Version;
+
+import org.apache.jackrabbit.util.Text;
 import org.apache.james.api.user.User;
 
+@Entity(name="User")
 public class JPAUser implements User {
 
+    /** 
+     * Static salt for hashing password.
+     * Modifying this value will render all passwords unrecognizable.
+     */
+    public static final String SALT = "JPAUsersRepository";
+    
+    /**
+     * Hashes salted password.
+     * @param username not null
+     * @param password not null
+     * @return not null
+     */
+    public static String hashPassword(String username, String password) {
+        // Combine dynamic and static salt
+        final String hashedSaltedPassword = Text.md5(Text.md5(username + password) + SALT);
+        return hashedSaltedPassword;
+    }
     
+    /** Prevents concurrent modification */
+    @SuppressWarnings("unused")
+    @Version
+    private int version;
     
+    /** Key by user name */
+    @Id
+    private String name;
+    /** Hashed password */
+    @Basic
+    private String password;
+    
+    protected JPAUser() {}
+    
+    public JPAUser(final String userName, String password) {
+        super();
+        this.name = userName;
+        this.password = hashPassword(userName, password);
+    }
+
     public String getUserName() {
-        return null;
+        return name;
+    }
+    
+    /**
+     * Gets salted, hashed password.
+     * @return the hashedSaltedPassword
+     */
+    public final String getHashedSaltedPassword() {
+        return password;
     }
 
     public boolean setPassword(String newPass) {
-        return false;
+        final boolean result;
+        if (newPass == null) {
+            result = false;
+        } else {
+            password = hashPassword(name, newPass);
+            result = true;
+        }
+        return result;
     }
 
     public boolean verifyPassword(String pass) {
-        return false;
+        final boolean result;
+        if (pass == null) {
+            result = password == null;
+        } else if (password == null) {
+            result = false;
+        } else {
+            result = password.equals(hashPassword(name, pass));
+        }
+        return result;
     }
-
+    
+    
 }

Modified: james/server/trunk/jpa-store/src/main/java/org/apache/james/server/jpa/JPAUsersRepository.java
URL: http://svn.apache.org/viewvc/james/server/trunk/jpa-store/src/main/java/org/apache/james/server/jpa/JPAUsersRepository.java?rev=713471&r1=713470&r2=713471&view=diff
==============================================================================
--- james/server/trunk/jpa-store/src/main/java/org/apache/james/server/jpa/JPAUsersRepository.java (original)
+++ james/server/trunk/jpa-store/src/main/java/org/apache/james/server/jpa/JPAUsersRepository.java Wed Nov 12 11:34:18 2008
@@ -19,13 +19,80 @@
 
 package org.apache.james.server.jpa;
 
+import java.util.Collections;
 import java.util.Iterator;
+import java.util.List;
 
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+import javax.persistence.PersistenceException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.james.api.user.User;
 import org.apache.james.api.user.UsersRepository;
 
+/**
+ * Proof-of-concept repository using JPA.
+ * TODO: Support managed contexts.
+ * TODO: Use factory and support pooled contexts
+ */
 public class JPAUsersRepository implements UsersRepository {
 
+    private static final Log LOGGER = LogFactory.getLog(JPAUsersRepository.class);
+
+    private Log logger = LOGGER;
+
+    private EntityManager entityManager;
+
+    /**
+     * Constructs repository with injection.
+     * @param entityManager not null
+     */
+    public JPAUsersRepository(EntityManager entityManager) {
+        super();
+        this.entityManager = entityManager;
+    }
+
+    /**
+     * Constructor for setting injection.
+     */
+    public JPAUsersRepository() {
+        this(null);
+    }
+
+    /**
+     * Gets current logger.
+     * @return the logger
+     */
+    public final Log getLogger() {
+        return logger;
+    }
+
+    /**
+     * Setter injection for logging.
+     * @param logger the logger to set
+     */
+    public final void setLogger(Log logger) {
+        this.logger = logger;
+    }
+
+    /**
+     * Gets entity manager.
+     * @return the entityManager
+     */
+    public final EntityManager getEntityManager() {
+        return entityManager;
+    }
+
+    /**
+     * Sets entity manager.
+     * @param entityManager the entityManager to set
+     */
+    public final void setEntityManager(EntityManager entityManager) {
+        this.entityManager = entityManager;
+    }
+
     /**
      * Adds a user to the repository with the specified User object.
      *
@@ -39,7 +106,7 @@
      * implementations of users object.
      */
     public boolean addUser(User user) {
-        return false;
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -53,9 +120,9 @@
      * eventually modified by retrieving it later.
      */
     public void addUser(String name, Object attributes) {
-        
+        throw new UnsupportedOperationException();
     }
-    
+
     /**
      * Adds a user to the repository with the specified password
      * 
@@ -66,6 +133,19 @@
      * @since James 2.3.0
      */
     public boolean addUser(String username, String password) {
+        final EntityTransaction transaction = entityManager.getTransaction();
+        try {
+            transaction.begin();
+            JPAUser user = new JPAUser(username, password);
+            entityManager.persist(user);
+            transaction.commit();
+            return true;
+        } catch (PersistenceException e) {
+            logger.debug("Failed to save user", e);
+            if (transaction.isActive()) {
+                transaction.rollback();
+            }
+        }
         return false;
     }
 
@@ -79,7 +159,19 @@
      * @since James 1.2.2
      */
     public User getUserByName(String name) {
-        return new JPAUser();
+        return getJPAUserByName(name);
+    }
+
+    private JPAUser getJPAUserByName(String name) {
+        try
+        {
+            return (JPAUser) entityManager.createQuery("SELECT user FROM User user WHERE user.name=?1")
+                            .setParameter(1, name)
+                            .getSingleResult();
+        } catch (PersistenceException e) {
+            logger.debug("Failed to find user", e);
+            return null;
+        }
     }
 
     /**
@@ -94,7 +186,7 @@
      * implementations and the getUserByName will search according to this property.
      */
     public User getUserByNameCaseInsensitive(String name) {
-        return new JPAUser();
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -115,6 +207,23 @@
      * @return true if successful.
      */
     public boolean updateUser(User user) {
+        final EntityTransaction transaction = entityManager.getTransaction();
+        try {
+            if (contains(user.getUserName())) {
+                transaction.begin();
+                entityManager.merge(user);
+                transaction.commit();
+            } else {
+                logger.debug("User not found");
+                return false;
+            }
+        } catch (PersistenceException e) {
+            logger.debug("Failed to update user", e);
+            if (transaction.isActive()) {
+                transaction.rollback();
+            }
+            return false;
+        }
         return true;
     }
 
@@ -124,7 +233,18 @@
      * @param name the user to remove from the repository
      */
     public void removeUser(String name) {
-        
+        final EntityTransaction transaction = entityManager.getTransaction();
+        try {
+            transaction.begin();
+            JPAUser user = getJPAUserByName(name);
+            entityManager.remove(user);
+            transaction.commit();
+        } catch (PersistenceException e) {
+            logger.debug("Failed to save user", e);
+            if (transaction.isActive()) {
+                transaction.rollback();
+            }
+        }
     }
 
     /**
@@ -134,7 +254,15 @@
      * @return whether the user is in the repository
      */
     public boolean contains(String name) {
-        return false;
+        try
+        {
+            return ((Long) entityManager.createQuery("SELECT COUNT(user) FROM User user WHERE user.name=?1")
+                            .setParameter(1, name)
+                            .getSingleResult()).longValue() > 0;
+        } catch (PersistenceException e) {
+            logger.debug("Failed to find user", e);
+            return false;
+        }
     }
 
     /**
@@ -148,7 +276,7 @@
      * implementations and the contains will search according to this property.
      */
     public boolean containsCaseInsensitive(String name) {
-        return false;
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -163,7 +291,17 @@
      * @since James 1.2.2
      */
     public boolean test(String name, String password) {
-        return false;
+        final JPAUser user = getJPAUserByName(name);
+        final boolean result;
+        if (user == null)
+        {
+            result = false;
+        }
+        else
+        {
+            result = user.verifyPassword(password);
+        }
+        return result;
     }
 
     /**
@@ -172,7 +310,14 @@
      * @return the number of users in the repository
      */
     public int countUsers() {
-        return 0;
+        try
+        {
+            return ((Long) entityManager.createQuery("SELECT COUNT(user) FROM User user")
+                            .getSingleResult()).intValue();
+        } catch (PersistenceException e) {
+            logger.debug("Failed to find user", e);
+            return 0;
+        }
     }
 
     /**
@@ -181,8 +326,28 @@
      * @return Iterator over a collection of Strings, each being one user in the repository.
      */
     public Iterator list() {
-        return null;
+        try
+        {
+            final List results = entityManager.createQuery("SELECT user FROM User user").getResultList();
+            return new Iterator() {
+                private final Iterator it = results.iterator();
+                public boolean hasNext() {
+                    return it.hasNext();
+                }
+
+                public Object next() {
+                    return ((JPAUser)it.next()).getUserName();
+                }
+
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+            };
+        } catch (PersistenceException e) {
+            logger.debug("Failed to find user", e);
+            return Collections.EMPTY_LIST.iterator();
+        }
     }
-    
+
 
 }

Added: james/server/trunk/jpa-store/src/test/java/org/apache/james/server/jpa/JpaUsersRepositoryTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/jpa-store/src/test/java/org/apache/james/server/jpa/JpaUsersRepositoryTest.java?rev=713471&view=auto
==============================================================================
--- james/server/trunk/jpa-store/src/test/java/org/apache/james/server/jpa/JpaUsersRepositoryTest.java (added)
+++ james/server/trunk/jpa-store/src/test/java/org/apache/james/server/jpa/JpaUsersRepositoryTest.java Wed Nov 12 11:34:18 2008
@@ -0,0 +1,85 @@
+/****************************************************************
+ * 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 org.apache.james.server.jpa;
+
+import java.util.HashMap;
+
+import javax.persistence.PersistenceException;
+
+import org.apache.james.api.user.UsersRepository;
+import org.apache.james.userrepository.MockUsersRepositoryTest;
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory;
+import org.apache.openjpa.persistence.OpenJPAEntityTransaction;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
+
+public class JpaUsersRepositoryTest extends MockUsersRepositoryTest {
+
+    private HashMap<String, String> properties;
+    private OpenJPAEntityManagerFactory factory;
+    private OpenJPAEntityManager manager;
+
+    @Override
+    protected void setUp() throws Exception {
+        properties = new HashMap<String, String>();
+        properties.put("openjpa.ConnectionDriverName", "org.h2.Driver");
+        properties.put("openjpa.ConnectionURL", "jdbc:h2:target/users/db");
+        properties.put("openjpa.Log", "JDBC=WARN, SQL=WARN, Runtime=WARN");
+        properties.put("openjpa.ConnectionFactoryProperties", "PrettyPrint=true, PrettyPrintLineLength=72");
+        properties.put("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)");
+        properties.put("openjpa.MetaDataFactory", "jpa(Types=org.apache.james.server.jpa.JPAUser)");
+        super.setUp();
+        deleteAll();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        deleteAll();
+        super.tearDown();
+        if (manager != null)
+        {
+            manager.close();
+        }
+        if (factory != null)
+        {
+            factory.close();
+        }
+    }
+    
+    private void deleteAll() {
+        try
+        {
+            OpenJPAEntityManager manager = factory.createEntityManager();
+            final OpenJPAEntityTransaction transaction = manager.getTransaction();
+            transaction.begin();
+            manager.createQuery("DELETE FROM User user").executeUpdate();
+            transaction.commit();
+        } catch (PersistenceException e) {
+            e.printStackTrace();
+        }
+    }
+
+    protected UsersRepository getUsersRepository() throws Exception 
+    {
+        factory = OpenJPAPersistence.getEntityManagerFactory(properties);
+        manager = factory.createEntityManager();
+        return new JPAUsersRepository(manager);
+    }
+}



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