Hi Robert,

shouldn't the equals(Object) and hashCode() method get overridden in JPAUser
to check for the right properties ?

Cheers,
Norman Maurer

2008/11/12 <[EMAIL PROTECTED]>

> 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;
>
> [EMAIL PROTECTED](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: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]
>
>

Reply via email to