Add JPA implementation based on NamedQueries
git-svn-id: https://svn.apache.org/repos/asf/continuum/branches/continuum-jpa-evenisse@652556 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/TODO.txt b/TODO.txt
new file mode 100644
index 0000000..ded53ef
--- /dev/null
+++ b/TODO.txt
@@ -0,0 +1,5 @@
+
+TODO for Continuum JPA store implementation
+-------------------------------------------
+
+1) Review JPA annotated Entities.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..3704867
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,55 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.continuum</groupId>
+ <artifactId>continuum-model-jpa</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-SNAPSHOT</version>
+ <name>continuum-model-jpa</name>
+ <url>http://maven.apache.org</url>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.4</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.openjpa</groupId>
+ <artifactId>openjpa</artifactId>
+ <version>1.0.1</version>
+ </dependency>
+ <dependency>
+ <groupId>hsqldb</groupId>
+ <artifactId>hsqldb</artifactId>
+ <version>1.8.0.7</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring</artifactId>
+ <version>2.5</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <version>2.5</version>
+ </dependency>
+
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/src/main/java/org/apache/continuum/dao/api/GenericDao.java b/src/main/java/org/apache/continuum/dao/api/GenericDao.java
new file mode 100644
index 0000000..af7cb41
--- /dev/null
+++ b/src/main/java/org/apache/continuum/dao/api/GenericDao.java
@@ -0,0 +1,34 @@
+package org.apache.continuum.dao.api;
+
+import org.springframework.dao.DataAccessException;
+import org.springframework.orm.ObjectRetrievalFailureException;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
+ * @version $Id$
+ */
+public interface GenericDao<T>
+{
+ Class<T> getPersistentClass();
+
+ T findById( long id )
+ throws ObjectRetrievalFailureException;
+
+ @SuppressWarnings("unchecked")
+ List<T> findAll();
+
+ @SuppressWarnings("unchecked")
+ List<T> findByNamedQueryAndNamedParams( Class<T> entityClass, String queryName, Map<String, Object> params )
+ throws DataAccessException;
+
+ T findUniqByNamedQueryAndNamedParams( Class<T> entityClass, String queryName, Map<String, Object> params );
+
+ T saveOrUpdate( T entity );
+
+ void delete( long id );
+
+ void delete( T entity );
+}
diff --git a/src/main/java/org/apache/continuum/dao/impl/DaoFactoryImpl.java b/src/main/java/org/apache/continuum/dao/impl/DaoFactoryImpl.java
new file mode 100644
index 0000000..2d05d18
--- /dev/null
+++ b/src/main/java/org/apache/continuum/dao/impl/DaoFactoryImpl.java
@@ -0,0 +1,28 @@
+package org.apache.continuum.dao.impl;
+
+import org.apache.continuum.dao.api.GenericDao;
+import org.apache.continuum.model.project.Project;
+import org.apache.continuum.model.project.ProjectGroup;
+import org.apache.continuum.model.project.ProjectNotifier;
+
+/**
+ * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
+ * @version $Id$
+ */
+public class DaoFactoryImpl
+{
+ public GenericDao<ProjectGroup> createProjectGroupDao()
+ {
+ return new GenericDaoJpa<ProjectGroup>( ProjectGroup.class );
+ }
+
+ public GenericDao<Project> createProjectDao()
+ {
+ return new GenericDaoJpa<Project>( Project.class );
+ }
+
+ public GenericDao<ProjectNotifier> createNotifierDao()
+ {
+ return new GenericDaoJpa<ProjectNotifier>( ProjectNotifier.class );
+ }
+}
diff --git a/src/main/java/org/apache/continuum/dao/impl/GenericDaoJpa.java b/src/main/java/org/apache/continuum/dao/impl/GenericDaoJpa.java
new file mode 100644
index 0000000..dd66907
--- /dev/null
+++ b/src/main/java/org/apache/continuum/dao/impl/GenericDaoJpa.java
@@ -0,0 +1,106 @@
+package org.apache.continuum.dao.impl;
+
+import org.apache.continuum.dao.api.GenericDao;
+import org.apache.continuum.model.CommonPersistableEntity;
+import org.springframework.dao.DataAccessException;
+import org.springframework.orm.ObjectRetrievalFailureException;
+import org.springframework.orm.jpa.JpaCallback;
+import org.springframework.orm.jpa.support.JpaDaoSupport;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+import javax.persistence.Query;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
+ * @version $Id$
+ */
+public class GenericDaoJpa<T extends CommonPersistableEntity>
+ extends JpaDaoSupport
+ implements GenericDao<T>
+{
+ private Class<T> entityClass;
+
+ public GenericDaoJpa()
+ {
+ }
+
+ public GenericDaoJpa( Class<T> type )
+ {
+ entityClass = type;
+ }
+
+ public Class<T> getPersistentClass()
+ {
+ return entityClass;
+ }
+
+ public T findById( long id )
+ throws ObjectRetrievalFailureException
+ {
+ return getJpaTemplate().find( getPersistentClass(), id );
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<T> findAll()
+ {
+ String q = "SELECT z FROM " + entityClass.getSimpleName() + " z";
+ return (List<T>) getJpaTemplate().find( q );
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<T> findByNamedQueryAndNamedParams( Class<T> entityClass, String queryName, Map<String, Object> params )
+ throws DataAccessException
+ {
+ return (List<T>) getJpaTemplate().findByNamedQueryAndNamedParams( queryName, params );
+ }
+
+ @SuppressWarnings("unchecked")
+ public T findUniqByNamedQueryAndNamedParams( Class<T> entityClass, final String queryName,
+ final Map<String, Object> params )
+ throws DataAccessException
+ {
+ JpaCallback cb = new JpaCallback()
+ {
+ public Object doInJpa( EntityManager entityManager )
+ throws PersistenceException
+ {
+ Query q = entityManager.createNamedQuery( queryName );
+ for ( String key : params.keySet() )
+ {
+ q.setParameter( key, params.get( key ) );
+ }
+ return q.getSingleResult();
+ }
+ };
+ return (T) getJpaTemplate().execute( cb );
+ }
+
+ @Transactional
+ public T saveOrUpdate( T entity )
+ {
+ if ( null == entity.getId() )
+ {
+ getJpaTemplate().persist( entity );
+ }
+ else
+ {
+ entity = getJpaTemplate().merge( entity );
+ }
+ return entity;
+ }
+
+ @Transactional
+ public void delete( long id )
+ {
+ delete( findById( id ) );
+ }
+
+ public void delete( T entity )
+ {
+ getJpaTemplate().remove( entity );
+ }
+}
diff --git a/src/main/java/org/apache/continuum/model/CommonCreatedEntity.java b/src/main/java/org/apache/continuum/model/CommonCreatedEntity.java
new file mode 100644
index 0000000..6b8264d
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/CommonCreatedEntity.java
@@ -0,0 +1,45 @@
+/**
+ *
+ */
+package org.apache.continuum.model;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@MappedSuperclass
+public abstract class CommonCreatedEntity extends CommonPersistableEntity
+{
+
+ /**
+ * Date the entity was created.
+ */
+ @Temporal( TemporalType.TIMESTAMP )
+ @Column( name = "DATE_CREATED" )
+ private Date dateCreated;
+
+ /**
+ * @return the dateCreated
+ */
+ public Date getDateCreated()
+ {
+ return dateCreated;
+ }
+
+ /**
+ * @param dateCreated
+ * the dateCreated to set
+ */
+ public void setDateCreated( Date dateCreated )
+ {
+ this.dateCreated = dateCreated;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/CommonPersistableEntity.java b/src/main/java/org/apache/continuum/model/CommonPersistableEntity.java
new file mode 100644
index 0000000..0907b3d
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/CommonPersistableEntity.java
@@ -0,0 +1,90 @@
+/**
+ *
+ */
+package org.apache.continuum.model;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+
+/**
+ * Common persistable entity.
+ *
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@MappedSuperclass
+public abstract class CommonPersistableEntity implements Serializable
+{
+
+ /**
+ * Unique persistence identifier.
+ * <p>
+ * This is <code>null</code> if not persisted.
+ */
+ @Id
+ @GeneratedValue( strategy = GenerationType.IDENTITY )
+ @Column( name = "ID" )
+ private Long id;
+
+ /**
+ * @return the id which is the unique persistence identifier.
+ */
+ public Long getId()
+ {
+ return id;
+ }
+
+ /**
+ * @param id
+ * Unique persistence identifier to set.
+ */
+ public void setId( Long id )
+ {
+ this.id = id;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ( ( id == null ) ? 0 : id.hashCode() );
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj )
+ {
+ if ( this == obj )
+ return true;
+ if ( obj == null )
+ return false;
+ if ( getClass() != obj.getClass() )
+ return false;
+ CommonPersistableEntity other = (CommonPersistableEntity) obj;
+ if ( id == null )
+ {
+ if ( other.id != null )
+ return false;
+ }
+ else if ( !id.equals( other.id ) )
+ return false;
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/CommonUpdatableEntity.java b/src/main/java/org/apache/continuum/model/CommonUpdatableEntity.java
new file mode 100644
index 0000000..d7f71a8
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/CommonUpdatableEntity.java
@@ -0,0 +1,70 @@
+/**
+ *
+ */
+package org.apache.continuum.model;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import javax.persistence.Version;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@MappedSuperclass
+public abstract class CommonUpdatableEntity extends CommonCreatedEntity
+{
+
+ /**
+ * Date the entity was last updated.
+ */
+ @Temporal( TemporalType.TIMESTAMP )
+ @Column( name = "DATE_UPDATED" )
+ private Date dateUpdated;
+
+ /**
+ * Version for optimistic locking.
+ */
+ @Version
+ @Column( name = "OBJ_VERSION" )
+ private long objectVersion;
+
+ /**
+ * @return the dateUpdated
+ */
+ public Date getDateUpdated()
+ {
+ return dateUpdated;
+ }
+
+ /**
+ * @param dateUpdated
+ * the dateUpdated to set
+ */
+ public void setDateUpdated( Date dateUpdated )
+ {
+ this.dateUpdated = dateUpdated;
+ }
+
+ /**
+ * @return the version
+ */
+ public long getObjectVersion()
+ {
+ return objectVersion;
+ }
+
+ /**
+ * @param version
+ * the version to set
+ */
+ public void setObjectVersion( long version )
+ {
+ this.objectVersion = version;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/project/BuildDefinition.java b/src/main/java/org/apache/continuum/model/project/BuildDefinition.java
new file mode 100644
index 0000000..d7d7274
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/project/BuildDefinition.java
@@ -0,0 +1,97 @@
+package org.apache.continuum.model.project;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+import org.apache.continuum.model.system.Profile;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "BUILD_DEFINITION")
+public class BuildDefinition
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field defaultForProject
+ */
+ @Basic
+ @Column(name = "FLG_DEFAULT_PROJECT", nullable = false)
+ private boolean defaultForProject = false;
+
+ /**
+ * Field goals
+ */
+ @Basic
+ @Column(name = "GOALS")
+ private String goals;
+
+ /**
+ * Field arguments
+ */
+ @Basic
+ @Column(name = "ARGUMENTS")
+ private String arguments;
+
+ /**
+ * Field buildFile
+ */
+ @Basic
+ @Column(name = "BUILD_FILE")
+ private String buildFile;
+
+ /**
+ * Field buildFresh
+ */
+ @Basic
+ @Column(name = "FLG_BUILD_FRESH", nullable = false)
+ private boolean buildFresh = false;
+
+ /**
+ * Field description
+ */
+ @Basic
+ @Column(name = "DESCRIPTION")
+ private String description;
+
+ /**
+ * Field type
+ */
+ @Basic
+ @Column(name = "TYPE")
+ private String type;
+
+ /**
+ * Field schedule
+ */
+ @OneToOne
+ private Schedule schedule;
+
+ /**
+ * Field profile
+ */
+ @OneToOne
+ private Profile profile;
+
+ /**
+ * Field alwaysBuild
+ */
+ @Basic
+ @Column(name = "FLG_ALWAYS_BUILD")
+ private boolean alwaysBuild = false;
+
+ /**
+ * Field template
+ */
+ @Basic
+ @Column(name = "FLG_TEMPLATE")
+ private boolean template = false;
+
+}
diff --git a/src/main/java/org/apache/continuum/model/project/BuildDefinitionTemplate.java b/src/main/java/org/apache/continuum/model/project/BuildDefinitionTemplate.java
new file mode 100644
index 0000000..154a941
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/project/BuildDefinitionTemplate.java
@@ -0,0 +1,116 @@
+package org.apache.continuum.model.project;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.List;
+
+/**
+ * Template which contains some buildDefinitions
+ *
+ * @version $Id$
+ */
+@Entity
+@Table(name = "BUILD_DEFINITION_TEMPLATE")
+public class BuildDefinitionTemplate
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field name
+ */
+ @Basic
+ @Column(name = "NAME", nullable = false)
+ private String name;
+
+ /**
+ * Field continuumDefault
+ */
+ @Basic
+ @Column(name = "FLG_CONTINUUM_DEFAULT", nullable = false)
+ private boolean continuumDefault = false;
+
+ /**
+ * Field type
+ * <p/>
+ * TODO: Enum?
+ */
+ @Basic
+ @Column(name = "TEMPLATE_TYPE")
+ private String type;
+
+ /**
+ * Field buildDefinitions
+ */
+ @OneToMany
+ private List<BuildDefinition> buildDefinitions;
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return the continuumDefault
+ */
+ public boolean isContinuumDefault()
+ {
+ return continuumDefault;
+ }
+
+ /**
+ * @param continuumDefault the continuumDefault to set
+ */
+ public void setContinuumDefault( boolean continuumDefault )
+ {
+ this.continuumDefault = continuumDefault;
+ }
+
+ /**
+ * @return the type
+ */
+ public String getType()
+ {
+ return type;
+ }
+
+ /**
+ * @param type the type to set
+ */
+ public void setType( String type )
+ {
+ this.type = type;
+ }
+
+ /**
+ * @return the buildDefinitions
+ */
+ public List<BuildDefinition> getBuildDefinitions()
+ {
+ return buildDefinitions;
+ }
+
+ /**
+ * @param buildDefinitions the buildDefinitions to set
+ */
+ public void setBuildDefinitions( List<BuildDefinition> buildDefinitions )
+ {
+ this.buildDefinitions = buildDefinitions;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/project/BuildResult.java b/src/main/java/org/apache/continuum/model/project/BuildResult.java
new file mode 100644
index 0000000..64be193
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/project/BuildResult.java
@@ -0,0 +1,336 @@
+package org.apache.continuum.model.project;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+import org.apache.continuum.model.scm.ScmResult;
+import org.apache.continuum.model.scm.TestResult;
+
+import javax.persistence.Basic;
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * This class is a single continuum build.
+ *
+ * @version $Id$
+ */
+@Entity
+@Table(name = "BUILD_RESULT")
+public class BuildResult
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field project
+ */
+ @ManyToOne
+ @JoinColumn(name = "ID_PROJECT")
+ private Project project;
+
+ /**
+ * Field buildDefinition
+ */
+ @OneToOne
+ @JoinColumn(name = "ID_BUILD_DEFINITION")
+ private BuildDefinition buildDefinition = null;
+
+ /**
+ * Field buildNumber
+ */
+ @Basic
+ @Column(name = "BUILD_NUMBER")
+ private int buildNumber = 0;
+
+ /**
+ * Field state.
+ * <p/>
+ * TODO: This is a candidate for enum.
+ */
+ @Basic
+ @Column(name = "RESULT_STATE")
+ private int state = 0;
+
+ /**
+ * Field trigger
+ * <p/>
+ * TODO: enum?
+ */
+ @Basic
+ @Column(name = "RESULT_TRIGGER")
+ private int trigger = 0;
+
+ /**
+ * Field startTime
+ */
+ @Temporal(TemporalType.TIME)
+ @Column(name = "START_TIME", nullable = false)
+ private Date startTime;
+
+ /**
+ * Field endTime
+ */
+ @Temporal(TemporalType.TIME)
+ @Column(name = "END_TIME", nullable = false)
+ private Date endTime;
+
+ /**
+ * Field error
+ */
+ @Basic
+ @Column(name = "ERROR")
+ private String error;
+
+ /**
+ * Field success
+ */
+ @Basic
+ @Column(name = "FLG_SUCCESS", nullable = false)
+ private boolean success = false;
+
+ /**
+ * Field exitCode
+ */
+ @Basic
+ @Column(name = "EXIT_CODE", nullable = false)
+ private int exitCode = 0;
+
+ /**
+ * Field scmResult
+ */
+ @OneToOne(fetch = FetchType.EAGER)
+ private ScmResult scmResult;
+
+ /**
+ * Field testResult
+ */
+ @OneToOne(fetch = FetchType.EAGER)
+ private TestResult testResult;
+
+ /**
+ * Field modifiedDependencies
+ */
+ @OneToMany(cascade = CascadeType.ALL)
+ @JoinTable(name = "PROJECT_DEPENDENCY", joinColumns = @JoinColumn(name = "BUILD_ID"),
+ inverseJoinColumns = @JoinColumn(name = "ID_PROJECT_DEPENDENCY"))
+ private List<ProjectDependency> modifiedDependencies;
+
+ /**
+ * @return the project
+ */
+ public Project getProject()
+ {
+ return project;
+ }
+
+ /**
+ * @param project the project to set
+ */
+ public void setProject( Project project )
+ {
+ this.project = project;
+ }
+
+ /**
+ * @return the buildDefinition
+ */
+ public BuildDefinition getBuildDefinition()
+ {
+ return buildDefinition;
+ }
+
+ /**
+ * @param buildDefinition the buildDefinition to set
+ */
+ public void setBuildDefinition( BuildDefinition buildDefinition )
+ {
+ this.buildDefinition = buildDefinition;
+ }
+
+ /**
+ * @return the buildNumber
+ */
+ public int getBuildNumber()
+ {
+ return buildNumber;
+ }
+
+ /**
+ * @param buildNumber the buildNumber to set
+ */
+ public void setBuildNumber( int buildNumber )
+ {
+ this.buildNumber = buildNumber;
+ }
+
+ /**
+ * @return the state
+ */
+ public int getState()
+ {
+ return state;
+ }
+
+ /**
+ * @param state the state to set
+ */
+ public void setState( int state )
+ {
+ this.state = state;
+ }
+
+ /**
+ * @return the trigger
+ */
+ public int getTrigger()
+ {
+ return trigger;
+ }
+
+ /**
+ * @param trigger the trigger to set
+ */
+ public void setTrigger( int trigger )
+ {
+ this.trigger = trigger;
+ }
+
+ /**
+ * @return the startTime
+ */
+ public Date getStartTime()
+ {
+ return startTime;
+ }
+
+ /**
+ * @param startTime the startTime to set
+ */
+ public void setStartTime( Date startTime )
+ {
+ this.startTime = startTime;
+ }
+
+ /**
+ * @return the endTime
+ */
+ public Date getEndTime()
+ {
+ return endTime;
+ }
+
+ /**
+ * @param endTime the endTime to set
+ */
+ public void setEndTime( Date endTime )
+ {
+ this.endTime = endTime;
+ }
+
+ /**
+ * @return the error
+ */
+ public String getError()
+ {
+ return error;
+ }
+
+ /**
+ * @param error the error to set
+ */
+ public void setError( String error )
+ {
+ this.error = error;
+ }
+
+ /**
+ * @return the success
+ */
+ public boolean isSuccess()
+ {
+ return success;
+ }
+
+ /**
+ * @param success the success to set
+ */
+ public void setSuccess( boolean success )
+ {
+ this.success = success;
+ }
+
+ /**
+ * @return the exitCode
+ */
+ public int getExitCode()
+ {
+ return exitCode;
+ }
+
+ /**
+ * @param exitCode the exitCode to set
+ */
+ public void setExitCode( int exitCode )
+ {
+ this.exitCode = exitCode;
+ }
+
+ /**
+ * @return the scmResult
+ */
+ public ScmResult getScmResult()
+ {
+ return scmResult;
+ }
+
+ /**
+ * @param scmResult the scmResult to set
+ */
+ public void setScmResult( ScmResult scmResult )
+ {
+ this.scmResult = scmResult;
+ }
+
+ /**
+ * @return the testResult
+ */
+ public TestResult getTestResult()
+ {
+ return testResult;
+ }
+
+ /**
+ * @param testResult the testResult to set
+ */
+ public void setTestResult( TestResult testResult )
+ {
+ this.testResult = testResult;
+ }
+
+ /**
+ * @return the modifiedDependencies
+ */
+ public List<ProjectDependency> getModifiedDependencies()
+ {
+ return modifiedDependencies;
+ }
+
+ /**
+ * @param modifiedDependencies the modifiedDependencies to set
+ */
+ public void setModifiedDependencies( List<ProjectDependency> modifiedDependencies )
+ {
+ this.modifiedDependencies = modifiedDependencies;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/project/ContinuumModelloMetadata.java b/src/main/java/org/apache/continuum/model/project/ContinuumModelloMetadata.java
new file mode 100644
index 0000000..06fb15b
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/project/ContinuumModelloMetadata.java
@@ -0,0 +1,23 @@
+package org.apache.continuum.model.project;
+
+// Model class imports
+
+/**
+ * Generated ModelloMetadata class for Continuum.
+ *
+ * @author Mr Modello
+ */
+public class ContinuumModelloMetadata
+{
+ private String modelVersion;
+
+ public String getModelVersion()
+ {
+ return modelVersion;
+ }
+
+ public void setModelVersion( String modelVersion )
+ {
+ this.modelVersion = modelVersion;
+ }
+}
diff --git a/src/main/java/org/apache/continuum/model/project/ContinuumProjectState.java b/src/main/java/org/apache/continuum/model/project/ContinuumProjectState.java
new file mode 100644
index 0000000..889586c
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/project/ContinuumProjectState.java
@@ -0,0 +1,102 @@
+package org.apache.continuum.model.project;
+
+/**
+ * null
+ *
+ * @version $Revision$ $Date$
+ */
+public class ContinuumProjectState implements java.io.Serializable
+{
+
+ /**
+ * Field name
+ */
+ private String name;
+
+ // -----------/
+ // - Methods -/
+ // -----------/
+
+ /**
+ * Get null
+ */
+ public String getName()
+ {
+ return this.name;
+ } // -- String getName()
+
+ /**
+ * Set null
+ *
+ * @param name
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ } // -- void setName(String)
+
+ public final static int NEW = 1;
+
+ public final static int OK = 2;
+
+ public final static int FAILED = 3;
+
+ public final static int ERROR = 4;
+
+ public final static int BUILDING = 6;
+
+ public final static int CHECKING_OUT = 7;
+
+ public final static int UPDATING = 8;
+
+ public final static int WARNING = 9;
+
+ public final static int CHECKEDOUT = 10;
+
+ // TODO: maybe move these to another class
+ public static final int TRIGGER_FORCED = 1;
+
+ // TODO: remove
+ public static final int TRIGGER_SCHEDULED = 0;
+
+ public static final int TRIGGER_UNKNOWN = TRIGGER_SCHEDULED;
+
+ public String getI18nKey()
+ {
+ return "org.apache.continuum.project.state." + name;
+ }
+
+ public boolean equals( Object object )
+ {
+ if ( !( object instanceof ContinuumProjectState ) )
+ {
+ return false;
+ }
+
+ ContinuumProjectState other = (ContinuumProjectState) object;
+
+ return name.equals( other.name );
+ }
+
+ public int hashCode()
+ {
+ return name.hashCode();
+ }
+
+ public String toString()
+ {
+ return name;
+ }
+
+ private String modelEncoding = "UTF-8";
+
+ public void setModelEncoding( String modelEncoding )
+ {
+ this.modelEncoding = modelEncoding;
+ }
+
+ public String getModelEncoding()
+ {
+ return modelEncoding;
+ }
+}
diff --git a/src/main/java/org/apache/continuum/model/project/Project.java b/src/main/java/org/apache/continuum/model/project/Project.java
new file mode 100644
index 0000000..02bb17d
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/project/Project.java
@@ -0,0 +1,599 @@
+package org.apache.continuum.model.project;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+import org.apache.continuum.model.scm.ScmResult;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.NamedQuery;
+import java.util.List;
+
+/**
+ * A Project registered in the system.
+ *
+ * @version $Id$
+ */
+@Entity
+@Table(name = "PROJECT")
+@NamedQueries(
+{
+ @NamedQuery(name = "Project.findAll", query = "SELECT p from Project as p"),
+ @NamedQuery(name = "Project.find", query = "SELECT p from Project as p WHERE p.groupId = :groupId AND p.artifactId = :artifactId AND p.version = :version")
+})
+
+public class Project
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field groupId
+ */
+ @Basic
+ @Column(name = "GROUP_ID")
+ private String groupId;
+
+ /**
+ * Field artifactId
+ */
+ @Basic
+ @Column(name = "ARTIFACT_ID")
+ private String artifactId;
+
+ /**
+ * Artifact version
+ */
+ @Basic
+ @Column(name = "VERSION")
+ private String version;
+
+ /**
+ * Field executorId
+ */
+ @Basic
+ @Column(name = "EXECUTOR_ID")
+ private String executorId;
+
+ /**
+ * Field name
+ */
+ @Basic
+ @Column(name = "NAME")
+ private String name;
+
+ /**
+ * Field description
+ */
+ @Basic
+ @Column(name = "DESCRIPTION")
+ private String description;
+
+ /**
+ * Field url
+ */
+ @Basic
+ @Column(name = "URL")
+ private String url;
+
+ /**
+ * Field scmUrl
+ */
+ @Basic
+ @Column(name = "SCM_URL")
+ private String scmUrl;
+
+ /**
+ * Field scmTag
+ */
+ @Basic
+ @Column(name = "SCM_TAG")
+ private String scmTag;
+
+ /**
+ * Field scmUsername
+ */
+ @Basic
+ @Column(name = "SCM_USERNAME")
+ private String scmUsername;
+
+ /**
+ * Field scmPassword
+ */
+ @Basic
+ @Column(name = "SCM_PASSWORD")
+ private String scmPassword;
+
+ /**
+ * Field scmUseCache
+ */
+ @Basic
+ @Column(name = "FLG_SCM_USE_CACHE", nullable = false)
+ private boolean scmUseCache = false;
+
+ /**
+ * Field state.
+ * <p/>
+ * TODO: Review! This is a candidate for an enum type.
+ */
+ @Basic
+ @Column(name = "STATE")
+ private int state = 1;
+
+ /**
+ * Field oldState
+ * <p/>
+ * TODO: Review! This is a candidate for an enum type.
+ */
+ @Basic
+ @Column(name = "OLD_STATE")
+ private int oldState = 0;
+
+ /**
+ * Field latestBuildId
+ */
+ @Basic
+ @Column(name = "LATEST_BUILD_ID")
+ private int latestBuildId = 0;
+
+ /**
+ * Field buildNumber
+ */
+ @Basic
+ @Column(name = "BUILD_NUMBER")
+ private int buildNumber = 0;
+
+ /**
+ * Field workingDirectory
+ */
+ @Basic
+ @Column(name = "WORKING_DIRECTORY")
+ private String workingDirectory;
+
+ /**
+ * Collection of {@link BuildResult}s for this {@link Project} instance.
+ */
+ @OneToMany(mappedBy = "project")
+ private List<BuildResult> buildResults;
+
+ /**
+ * Field checkoutResult
+ */
+ @OneToOne
+ @JoinColumn(name = "ID_CHECKOUT_RESULT")
+ private ScmResult checkoutResult;
+
+ /**
+ * Field developers
+ * <p/>
+ * TODO:
+ */
+ @OneToMany
+ private List<ProjectDeveloper> developers;
+
+ /**
+ * Field parent
+ */
+ @OneToOne
+ @JoinColumn(name = "ID_PARENT")
+ private ProjectDependency parent;
+
+ /**
+ * Field dependencies
+ */
+ @OneToMany
+ private List<ProjectDependency> dependencies;
+
+ /**
+ * Field projectGroup
+ */
+ @ManyToOne
+ @JoinColumn(name = "ID_PROJECT_GROUP")
+ private ProjectGroup projectGroup;
+
+ /**
+ * Field notifiers
+ */
+ @OneToMany
+ private List<ProjectNotifier> notifiers;
+
+ public void addNotifier( ProjectNotifier n )
+ {
+ notifiers.add( n );
+ }
+
+ public void removeNotifier( ProjectNotifier notifier )
+ {
+ notifiers.remove( notifier );
+ }
+
+ /**
+ * @return the groupId
+ */
+ public String getGroupId()
+ {
+ return groupId;
+ }
+
+ /**
+ * @param groupId the groupId to set
+ */
+ public void setGroupId( String groupId )
+ {
+ this.groupId = groupId;
+ }
+
+ /**
+ * @return the artifactId
+ */
+ public String getArtifactId()
+ {
+ return artifactId;
+ }
+
+ /**
+ * @param artifactId the artifactId to set
+ */
+ public void setArtifactId( String artifactId )
+ {
+ this.artifactId = artifactId;
+ }
+
+ /**
+ * @return the version
+ */
+ public String getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * @param version the version to set
+ */
+ public void setVersion( String version )
+ {
+ this.version = version;
+ }
+
+ /**
+ * @return the executorId
+ */
+ public String getExecutorId()
+ {
+ return executorId;
+ }
+
+ /**
+ * @param executorId the executorId to set
+ */
+ public void setExecutorId( String executorId )
+ {
+ this.executorId = executorId;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return the description
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @param description the description to set
+ */
+ public void setDescription( String description )
+ {
+ this.description = description;
+ }
+
+ /**
+ * @return the url
+ */
+ public String getUrl()
+ {
+ return url;
+ }
+
+ /**
+ * @param url the url to set
+ */
+ public void setUrl( String url )
+ {
+ this.url = url;
+ }
+
+ /**
+ * @return the scmUrl
+ */
+ public String getScmUrl()
+ {
+ return scmUrl;
+ }
+
+ /**
+ * @param scmUrl the scmUrl to set
+ */
+ public void setScmUrl( String scmUrl )
+ {
+ this.scmUrl = scmUrl;
+ }
+
+ /**
+ * @return the scmTag
+ */
+ public String getScmTag()
+ {
+ return scmTag;
+ }
+
+ /**
+ * @param scmTag the scmTag to set
+ */
+ public void setScmTag( String scmTag )
+ {
+ this.scmTag = scmTag;
+ }
+
+ /**
+ * @return the scmUsername
+ */
+ public String getScmUsername()
+ {
+ return scmUsername;
+ }
+
+ /**
+ * @param scmUsername the scmUsername to set
+ */
+ public void setScmUsername( String scmUsername )
+ {
+ this.scmUsername = scmUsername;
+ }
+
+ /**
+ * @return the scmPassword
+ */
+ public String getScmPassword()
+ {
+ return scmPassword;
+ }
+
+ /**
+ * @param scmPassword the scmPassword to set
+ */
+ public void setScmPassword( String scmPassword )
+ {
+ this.scmPassword = scmPassword;
+ }
+
+ /**
+ * @return the scmUseCache
+ */
+ public boolean isScmUseCache()
+ {
+ return scmUseCache;
+ }
+
+ /**
+ * @param scmUseCache the scmUseCache to set
+ */
+ public void setScmUseCache( boolean scmUseCache )
+ {
+ this.scmUseCache = scmUseCache;
+ }
+
+ /**
+ * @return the state
+ */
+ public int getState()
+ {
+ return state;
+ }
+
+ /**
+ * @param state the state to set
+ */
+ public void setState( int state )
+ {
+ this.state = state;
+ }
+
+ /**
+ * @return the oldState
+ */
+ public int getOldState()
+ {
+ return oldState;
+ }
+
+ /**
+ * @param oldState the oldState to set
+ */
+ public void setOldState( int oldState )
+ {
+ this.oldState = oldState;
+ }
+
+ /**
+ * @return the latestBuildId
+ */
+ public int getLatestBuildId()
+ {
+ return latestBuildId;
+ }
+
+ /**
+ * @param latestBuildId the latestBuildId to set
+ */
+ public void setLatestBuildId( int latestBuildId )
+ {
+ this.latestBuildId = latestBuildId;
+ }
+
+ /**
+ * @return the buildNumber
+ */
+ public int getBuildNumber()
+ {
+ return buildNumber;
+ }
+
+ /**
+ * @param buildNumber the buildNumber to set
+ */
+ public void setBuildNumber( int buildNumber )
+ {
+ this.buildNumber = buildNumber;
+ }
+
+ /**
+ * @return the workingDirectory
+ */
+ public String getWorkingDirectory()
+ {
+ return workingDirectory;
+ }
+
+ /**
+ * @param workingDirectory the workingDirectory to set
+ */
+ public void setWorkingDirectory( String workingDirectory )
+ {
+ this.workingDirectory = workingDirectory;
+ }
+
+ /**
+ * @return the buildResults
+ */
+ public List<BuildResult> getBuildResults()
+ {
+ return buildResults;
+ }
+
+ /**
+ * @param buildResults the buildResults to set
+ */
+ public void setBuildResults( List<BuildResult> buildResults )
+ {
+ this.buildResults = buildResults;
+ }
+
+ /**
+ * @return the checkoutResult
+ */
+ public ScmResult getCheckoutResult()
+ {
+ return checkoutResult;
+ }
+
+ /**
+ * @param checkoutResult the checkoutResult to set
+ */
+ public void setCheckoutResult( ScmResult checkoutResult )
+ {
+ this.checkoutResult = checkoutResult;
+ }
+
+ /**
+ * @return the developers
+ */
+ public List<ProjectDeveloper> getDevelopers()
+ {
+ return developers;
+ }
+
+ /**
+ * @param developers the developers to set
+ */
+ public void setDevelopers( List<ProjectDeveloper> developers )
+ {
+ this.developers = developers;
+ }
+
+ /**
+ * @return the parent
+ */
+ public ProjectDependency getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * @param parent the parent to set
+ */
+ public void setParent( ProjectDependency parent )
+ {
+ this.parent = parent;
+ }
+
+ /**
+ * @return the dependencies
+ */
+ public List<ProjectDependency> getDependencies()
+ {
+ return dependencies;
+ }
+
+ /**
+ * @param dependencies the dependencies to set
+ */
+ public void setDependencies( List<ProjectDependency> dependencies )
+ {
+ this.dependencies = dependencies;
+ }
+
+ /**
+ * @return the projectGroup
+ */
+ public ProjectGroup getProjectGroup()
+ {
+ return projectGroup;
+ }
+
+ /**
+ * @param projectGroup the projectGroup to set
+ */
+ public void setProjectGroup( ProjectGroup projectGroup )
+ {
+ this.projectGroup = projectGroup;
+ }
+
+ /**
+ * @return the notifiers
+ */
+ public List<ProjectNotifier> getNotifiers()
+ {
+ return notifiers;
+ }
+
+ /**
+ * @param notifiers the notifiers to set
+ */
+ public void setNotifiers( List<ProjectNotifier> notifiers )
+ {
+ this.notifiers = notifiers;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/project/ProjectDependency.java b/src/main/java/org/apache/continuum/model/project/ProjectDependency.java
new file mode 100644
index 0000000..8acbc6f
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/project/ProjectDependency.java
@@ -0,0 +1,89 @@
+package org.apache.continuum.model.project;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "PROJECT_DEPENDENCY")
+public class ProjectDependency
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field groupId
+ */
+ @Basic
+ @Column(name = "GROUP_ID")
+ private String groupId;
+
+ /**
+ * Field artifactId
+ */
+ @Basic
+ @Column(name = "ARTIFACT_ID")
+ private String artifactId;
+
+ /**
+ * Field version
+ */
+ @Basic
+ @Column(name = "VERSION")
+ private String version;
+
+ /**
+ * @return the groupId
+ */
+ public String getGroupId()
+ {
+ return groupId;
+ }
+
+ /**
+ * @param groupId the groupId to set
+ */
+ public void setGroupId( String groupId )
+ {
+ this.groupId = groupId;
+ }
+
+ /**
+ * @return the artifactId
+ */
+ public String getArtifactId()
+ {
+ return artifactId;
+ }
+
+ /**
+ * @param artifactId the artifactId to set
+ */
+ public void setArtifactId( String artifactId )
+ {
+ this.artifactId = artifactId;
+ }
+
+ /**
+ * @return the version
+ */
+ public String getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * @param version the version to set
+ */
+ public void setVersion( String version )
+ {
+ this.version = version;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/project/ProjectDeveloper.java b/src/main/java/org/apache/continuum/model/project/ProjectDeveloper.java
new file mode 100644
index 0000000..66532ed
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/project/ProjectDeveloper.java
@@ -0,0 +1,48 @@
+package org.apache.continuum.model.project;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "PROJECT_DEVELOPER")
+public class ProjectDeveloper
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field scmId
+ */
+ @Basic
+ @Column(name = "SCM_USERNAME")
+ private String scmId;
+
+ /**
+ * Field name
+ */
+ @Basic
+ @Column(name = "NAME")
+ private String name;
+
+ /**
+ * Field email
+ */
+ @Basic
+ @Column(name = "EMAIL")
+ private String email;
+
+ /**
+ * Field continuumId
+ */
+ @Basic
+ @Column(name = "CONTINUUM_ID", nullable = false)
+ private int continuumId = 0;
+
+}
diff --git a/src/main/java/org/apache/continuum/model/project/ProjectGroup.java b/src/main/java/org/apache/continuum/model/project/ProjectGroup.java
new file mode 100644
index 0000000..d6d2067
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/project/ProjectGroup.java
@@ -0,0 +1,189 @@
+package org.apache.continuum.model.project;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "PROJECT_GROUP")
+@NamedQueries(
+{
+ @NamedQuery ( name = "ProjectGroup.findProjectGroup", query = "SELECT pg FROM ProjectGroup as pg WHERE pg.groupId = :groupId"),
+ @NamedQuery ( name = "ProjectGroup.findProjects", query = "SELECT pg.projects FROM ProjectGroup as pg WHERE pg = :projectGroup")
+})
+public class ProjectGroup
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field groupId
+ */
+ @Basic
+ @Column(name = "GROUP_ID")
+ private String groupId;
+
+ /**
+ * Field name
+ */
+ @Basic
+ @Column(name = "NAME")
+ private String name;
+
+ /**
+ * Field description
+ */
+ @Basic
+ @Column(name = "DESCRIPTION")
+ private String description;
+
+ /**
+ * Field projects
+ */
+ @OneToMany(mappedBy = "projectGroup")
+ private List<Project> projects;
+
+ /**
+ * Field notifiers
+ */
+ @OneToMany
+ private List<ProjectNotifier> notifiers;
+
+ /**
+ * Field buildDefinitions
+ */
+ @OneToMany
+ private List<BuildDefinition> buildDefinitions;
+
+ public void addProject( Project p )
+ {
+ projects.add( p );
+ }
+
+ public void addNotifier( ProjectNotifier n )
+ {
+ notifiers.add( n );
+ }
+
+ public void removeProject( Project p )
+ {
+ projects.remove( p );
+ }
+
+ public void removeNotifier( ProjectNotifier notifier )
+ {
+ notifiers.remove( notifier );
+ }
+
+ /**
+ * @return the groupId
+ */
+ public String getGroupId()
+ {
+ return groupId;
+ }
+
+ /**
+ * @param groupId the groupId to set
+ */
+ public void setGroupId( String groupId )
+ {
+ this.groupId = groupId;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return the description
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @param description the description to set
+ */
+ public void setDescription( String description )
+ {
+ this.description = description;
+ }
+
+ /**
+ * @return the projects
+ */
+ public List<Project> getProjects()
+ {
+ if ( null == this.projects )
+ {
+ this.projects = new ArrayList<Project>();
+ }
+ return projects;
+ }
+
+ /**
+ * @param projects the projects to set
+ */
+ public void setProjects( List<Project> projects )
+ {
+ this.projects = projects;
+ }
+
+ /**
+ * @return the notifiers
+ */
+ public List<ProjectNotifier> getNotifiers()
+ {
+ return notifiers;
+ }
+
+ /**
+ * @param notifiers the notifiers to set
+ */
+ public void setNotifiers( List<ProjectNotifier> notifiers )
+ {
+ this.notifiers = notifiers;
+ }
+
+ /**
+ * @return the buildDefinitions
+ */
+ public List<BuildDefinition> getBuildDefinitions()
+ {
+ return buildDefinitions;
+ }
+
+ /**
+ * @param buildDefinitions the buildDefinitions to set
+ */
+ public void setBuildDefinitions( List<BuildDefinition> buildDefinitions )
+ {
+ this.buildDefinitions = buildDefinitions;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/project/ProjectNotifier.java b/src/main/java/org/apache/continuum/model/project/ProjectNotifier.java
new file mode 100644
index 0000000..3e563eb
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/project/ProjectNotifier.java
@@ -0,0 +1,119 @@
+package org.apache.continuum.model.project;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import java.util.Map;
+
+/**
+ * Configures one method for notifying users/developers when a build breaks.
+ * <p/>
+ * TODO: Review this to use discriminators for different Notifier extensions.
+ *
+ * @version $Id$
+ */
+@Entity
+@Table(name = "PROJECT_NOTIFIER")
+@NamedQueries
+({
+ @NamedQuery( name = "Notifier.findAllFromProject", query = "SELECT p.notifiers from Project as p WHERE p.id = :projectId"),
+ @NamedQuery( name = "Notifier.findAllFromProjectGroup", query = "SELECT pg.notifiers from ProjectGroup as pg WHERE pg.id = :projectGroupId")
+})
+public class ProjectNotifier
+ extends CommonUpdatableEntity
+{
+ /**
+ * Field type
+ * <p/>
+ * TODO: This is a candidate for enum type.
+ */
+ @Column(name = "GROUP_ID")
+ private String type = "mail";
+
+ /**
+ * Field from
+ */
+ @Column(name = "NOTIFIER_ORIGIN", nullable = false)
+ private int from = 0;
+
+ /**
+ * Field enabled
+ */
+ @Column(name = "FLG_ENABLED", nullable = false)
+ private boolean enabled = true;
+
+ /**
+ * Field recipientType
+ */
+ @Column(name = "RECIPIENT_TYPE", nullable = false)
+ private int recipientType = 0;
+
+ /**
+ * Field sendOnSuccess
+ */
+ @Column(name = "FLG_SEND_ON_SUCCESS", nullable = false)
+ private boolean sendOnSuccess = true;
+
+ /**
+ * Field sendOnFailure
+ */
+ @Column(name = "FLG_SEND_ON_FAILURE", nullable = false)
+ private boolean sendOnFailure = true;
+
+ /**
+ * Field sendOnError
+ */
+ @Column(name = "FLG_SEND_ON_ERROR", nullable = false)
+ private boolean sendOnError = true;
+
+ /**
+ * Field sendOnWarning
+ */
+ @Column(name = "FLG_SEND_ON_WARNING", nullable = false)
+ private boolean sendOnWarning = true;
+
+ /**
+ * Field configuration.
+ * <p/>
+ * TODO: Map!
+ */
+ @Transient
+ private Map configuration;
+
+ /**
+ * TODO: Map! Enum?
+ */
+ public static final int FROM_PROJECT = 1;
+
+ /**
+ * TODO: Map! Enum?
+ */
+ public static final int FROM_USER = 2;
+
+ /**
+ * Method toString
+ */
+ public java.lang.String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "id = '" );
+ buf.append( getId() + "'" );
+ return buf.toString();
+ } // -- java.lang.String toString()
+
+ public boolean isFromProject()
+ {
+ return from == FROM_PROJECT;
+ }
+
+ public boolean isFromUser()
+ {
+ return from == FROM_USER;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/project/Schedule.java b/src/main/java/org/apache/continuum/model/project/Schedule.java
new file mode 100644
index 0000000..fcdc8c7
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/project/Schedule.java
@@ -0,0 +1,158 @@
+package org.apache.continuum.model.project;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "BUILD_SCHEDULE")
+public class Schedule
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field active
+ */
+ @Basic
+ @Column(name = "FLG_ACTIVE", nullable = false)
+ private boolean active = false;
+
+ /**
+ * Field name
+ */
+ @Basic
+ @Column(name = "NAME", nullable = false)
+ private String name;
+
+ /**
+ * Field description
+ */
+ @Basic
+ @Column(name = "DESCRIPTION")
+ private String description;
+
+ /**
+ * Field delay
+ */
+ @Basic
+ @Column(name = "SCHEDULE_DELAY")
+ private int delay = 0;
+
+ /**
+ * Field maxJobExecutionTime
+ */
+ @Basic
+ @Column(name = "MAX_JOB_EXECUTION_TIME")
+ private int maxJobExecutionTime = 3600;
+
+ /**
+ * Field cronExpression
+ */
+ @Basic
+ @Column(name = "CRON_EXPRESSION")
+ private String cronExpression;
+
+ /**
+ * @return the active
+ */
+ public boolean isActive()
+ {
+ return active;
+ }
+
+ /**
+ * @param active the active to set
+ */
+ public void setActive( boolean active )
+ {
+ this.active = active;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return the description
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @param description the description to set
+ */
+ public void setDescription( String description )
+ {
+ this.description = description;
+ }
+
+ /**
+ * @return the delay
+ */
+ public int getDelay()
+ {
+ return delay;
+ }
+
+ /**
+ * @param delay the delay to set
+ */
+ public void setDelay( int delay )
+ {
+ this.delay = delay;
+ }
+
+ /**
+ * @return the maxJobExecutionTime
+ */
+ public int getMaxJobExecutionTime()
+ {
+ return maxJobExecutionTime;
+ }
+
+ /**
+ * @param maxJobExecutionTime the maxJobExecutionTime to set
+ */
+ public void setMaxJobExecutionTime( int maxJobExecutionTime )
+ {
+ this.maxJobExecutionTime = maxJobExecutionTime;
+ }
+
+ /**
+ * @return the cronExpression
+ */
+ public String getCronExpression()
+ {
+ return cronExpression;
+ }
+
+ /**
+ * @param cronExpression the cronExpression to set
+ */
+ public void setCronExpression( String cronExpression )
+ {
+ this.cronExpression = cronExpression;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/scm/ChangeFile.java b/src/main/java/org/apache/continuum/model/scm/ChangeFile.java
new file mode 100644
index 0000000..e8aba50
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/scm/ChangeFile.java
@@ -0,0 +1,91 @@
+package org.apache.continuum.model.scm;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "CHANGE_FILE")
+public class ChangeFile
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field name
+ */
+ @Basic
+ @Column(name = "NAME")
+ private String name;
+
+ /**
+ * Field revision
+ */
+ @Basic
+ @Column(name = "REVISION")
+ private String revision;
+
+ /**
+ * Field status
+ * <p/>
+ * TODO: Enum?
+ */
+ @Basic
+ @Column(name = "STATUS")
+ private String status;
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return the revision
+ */
+ public String getRevision()
+ {
+ return revision;
+ }
+
+ /**
+ * @param revision the revision to set
+ */
+ public void setRevision( String revision )
+ {
+ this.revision = revision;
+ }
+
+ /**
+ * @return the status
+ */
+ public String getStatus()
+ {
+ return status;
+ }
+
+ /**
+ * @param status the status to set
+ */
+ public void setStatus( String status )
+ {
+ this.status = status;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/scm/ChangeSet.java b/src/main/java/org/apache/continuum/model/scm/ChangeSet.java
new file mode 100644
index 0000000..c556cb0
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/scm/ChangeSet.java
@@ -0,0 +1,116 @@
+package org.apache.continuum.model.scm;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "CHANGE_SET")
+public class ChangeSet
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field author
+ */
+ @Basic
+ @Column(name = "AUTHOR")
+ private String author;
+
+ /**
+ * Field comment
+ */
+ @Basic
+ @Column(name = "COMMENT_TEXT")
+ private String comment;
+
+ /**
+ * Field date
+ */
+ @Temporal(TemporalType.TIME)
+ @Column(name = "DATE", nullable = false)
+ private Date date;
+
+ /**
+ * Field files
+ */
+ @OneToMany
+ private List<ChangeFile> files;
+
+ /**
+ * @return the author
+ */
+ public String getAuthor()
+ {
+ return author;
+ }
+
+ /**
+ * @param author the author to set
+ */
+ public void setAuthor( String author )
+ {
+ this.author = author;
+ }
+
+ /**
+ * @return the comment
+ */
+ public String getComment()
+ {
+ return comment;
+ }
+
+ /**
+ * @param comment the comment to set
+ */
+ public void setComment( String comment )
+ {
+ this.comment = comment;
+ }
+
+ /**
+ * @return the date
+ */
+ public Date getDate()
+ {
+ return date;
+ }
+
+ /**
+ * @param date the date to set
+ */
+ public void setDate( Date date )
+ {
+ this.date = date;
+ }
+
+ /**
+ * @return the files
+ */
+ public List<ChangeFile> getFiles()
+ {
+ return files;
+ }
+
+ /**
+ * @param files the files to set
+ */
+ public void setFiles( List<ChangeFile> files )
+ {
+ this.files = files;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/scm/ScmResult.java b/src/main/java/org/apache/continuum/model/scm/ScmResult.java
new file mode 100644
index 0000000..ce44bca
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/scm/ScmResult.java
@@ -0,0 +1,159 @@
+package org.apache.continuum.model.scm;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.List;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "SCM_RESULT")
+public class ScmResult
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field success
+ */
+ @Basic
+ @Column(name = "FLG_SUCCESS")
+ private boolean success = false;
+
+ /**
+ * Field commandLine
+ */
+ @Basic
+ @Column(name = "COMMAND_LINE")
+ private String commandLine;
+
+ /**
+ * Field providerMessage
+ */
+ @Basic
+ @Column(name = "PROVIDER_MESSAGE")
+ private String providerMessage;
+
+ /**
+ * Field commandOutput
+ */
+ @Basic
+ @Column(name = "COMMAND_OUTPUT")
+ private String commandOutput;
+
+ /**
+ * Field exception XXX: Renamed from 'EXCEPTION' to 'EXCEPTION_MSG'
+ */
+ @Basic
+ @Column(name = "EXCEPTION_MSG")
+ private String exception;
+
+ /**
+ * Field changes
+ */
+ @OneToMany
+ private List<ChangeSet> changes;
+
+ /**
+ * @return the success
+ */
+ public boolean isSuccess()
+ {
+ return success;
+ }
+
+ /**
+ * @param success the success to set
+ */
+ public void setSuccess( boolean success )
+ {
+ this.success = success;
+ }
+
+ /**
+ * @return the commandLine
+ */
+ public String getCommandLine()
+ {
+ return commandLine;
+ }
+
+ /**
+ * @param commandLine the commandLine to set
+ */
+ public void setCommandLine( String commandLine )
+ {
+ this.commandLine = commandLine;
+ }
+
+ /**
+ * @return the providerMessage
+ */
+ public String getProviderMessage()
+ {
+ return providerMessage;
+ }
+
+ /**
+ * @param providerMessage the providerMessage to set
+ */
+ public void setProviderMessage( String providerMessage )
+ {
+ this.providerMessage = providerMessage;
+ }
+
+ /**
+ * @return the commandOutput
+ */
+ public String getCommandOutput()
+ {
+ return commandOutput;
+ }
+
+ /**
+ * @param commandOutput the commandOutput to set
+ */
+ public void setCommandOutput( String commandOutput )
+ {
+ this.commandOutput = commandOutput;
+ }
+
+ /**
+ * @return the exception
+ */
+ public String getException()
+ {
+ return exception;
+ }
+
+ /**
+ * @param exception the exception to set
+ */
+ public void setException( String exception )
+ {
+ this.exception = exception;
+ }
+
+ /**
+ * @return the changes
+ */
+ public List<ChangeSet> getChanges()
+ {
+ return changes;
+ }
+
+ /**
+ * @param changes the changes to set
+ */
+ public void setChanges( List<ChangeSet> changes )
+ {
+ this.changes = changes;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/scm/SuiteResult.java b/src/main/java/org/apache/continuum/model/scm/SuiteResult.java
new file mode 100644
index 0000000..2baf4b1
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/scm/SuiteResult.java
@@ -0,0 +1,136 @@
+package org.apache.continuum.model.scm;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.List;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "SUITE_RESULT")
+public class SuiteResult
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field name
+ */
+ @Basic
+ @Column(name = "NAME")
+ private String name;
+
+ /**
+ * Field testCount
+ */
+ @Basic
+ @Column(name = "TEST_COUNT")
+ private int testCount = 0;
+
+ /**
+ * Field failureCount
+ */
+ @Basic
+ @Column(name = "FAILURE_COUNT")
+ private int failureCount = 0;
+
+ /**
+ * Field totalTime
+ */
+ @Basic
+ @Column(name = "TOTAL_TIME")
+ private long totalTime = 0;
+
+ /**
+ * Field failures
+ */
+ @OneToMany
+ private List<TestCaseFailure> failures;
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return the testCount
+ */
+ public int getTestCount()
+ {
+ return testCount;
+ }
+
+ /**
+ * @param testCount the testCount to set
+ */
+ public void setTestCount( int testCount )
+ {
+ this.testCount = testCount;
+ }
+
+ /**
+ * @return the failureCount
+ */
+ public int getFailureCount()
+ {
+ return failureCount;
+ }
+
+ /**
+ * @param failureCount the failureCount to set
+ */
+ public void setFailureCount( int failureCount )
+ {
+ this.failureCount = failureCount;
+ }
+
+ /**
+ * @return the totalTime
+ */
+ public long getTotalTime()
+ {
+ return totalTime;
+ }
+
+ /**
+ * @param totalTime the totalTime to set
+ */
+ public void setTotalTime( long totalTime )
+ {
+ this.totalTime = totalTime;
+ }
+
+ /**
+ * @return the failures
+ */
+ public List<TestCaseFailure> getFailures()
+ {
+ return failures;
+ }
+
+ /**
+ * @param failures the failures to set
+ */
+ public void setFailures( List<TestCaseFailure> failures )
+ {
+ this.failures = failures;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/scm/TestCaseFailure.java b/src/main/java/org/apache/continuum/model/scm/TestCaseFailure.java
new file mode 100644
index 0000000..50e418f
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/scm/TestCaseFailure.java
@@ -0,0 +1,66 @@
+package org.apache.continuum.model.scm;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "TEST_CASE_FAILURE")
+public class TestCaseFailure
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field name
+ */
+ @Basic
+ @Column(name = "NAME")
+ private String name;
+
+ /**
+ * Field exception
+ */
+ @Basic
+ @Column(name = "EXCEPTION")
+ private String exception;
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return the exception
+ */
+ public String getException()
+ {
+ return exception;
+ }
+
+ /**
+ * @param exception the exception to set
+ */
+ public void setException( String exception )
+ {
+ this.exception = exception;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/scm/TestResult.java b/src/main/java/org/apache/continuum/model/scm/TestResult.java
new file mode 100644
index 0000000..7af4c2a
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/scm/TestResult.java
@@ -0,0 +1,113 @@
+package org.apache.continuum.model.scm;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.List;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "TEST_RESULT")
+public class TestResult
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field testCount
+ */
+ @Basic
+ @Column(name = "TEST_COUNT", nullable = false)
+ private int testCount = 0;
+
+ /**
+ * Field failureCount
+ */
+ @Basic
+ @Column(name = "FAILURE_COUNT", nullable = false)
+ private int failureCount = 0;
+
+ /**
+ * Field totalTime
+ */
+ @Basic
+ @Column(name = "TOTAL_TIME", nullable = false)
+ private long totalTime = 0;
+
+ /**
+ * Field suiteResults
+ */
+ @OneToMany
+ private List<SuiteResult> suiteResults;
+
+ /**
+ * @return the testCount
+ */
+ public int getTestCount()
+ {
+ return testCount;
+ }
+
+ /**
+ * @param testCount the testCount to set
+ */
+ public void setTestCount( int testCount )
+ {
+ this.testCount = testCount;
+ }
+
+ /**
+ * @return the failureCount
+ */
+ public int getFailureCount()
+ {
+ return failureCount;
+ }
+
+ /**
+ * @param failureCount the failureCount to set
+ */
+ public void setFailureCount( int failureCount )
+ {
+ this.failureCount = failureCount;
+ }
+
+ /**
+ * @return the totalTime
+ */
+ public long getTotalTime()
+ {
+ return totalTime;
+ }
+
+ /**
+ * @param totalTime the totalTime to set
+ */
+ public void setTotalTime( long totalTime )
+ {
+ this.totalTime = totalTime;
+ }
+
+ /**
+ * @return the suiteResults
+ */
+ public List<SuiteResult> getSuiteResults()
+ {
+ return suiteResults;
+ }
+
+ /**
+ * @param suiteResults the suiteResults to set
+ */
+ public void setSuiteResults( List<SuiteResult> suiteResults )
+ {
+ this.suiteResults = suiteResults;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/system/ContinuumDatabase.java b/src/main/java/org/apache/continuum/model/system/ContinuumDatabase.java
new file mode 100644
index 0000000..9f224b8
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/system/ContinuumDatabase.java
@@ -0,0 +1,290 @@
+package org.apache.continuum.model.system;
+
+import java.io.Serializable;
+
+import org.apache.continuum.model.project.ProjectGroup;
+import org.apache.continuum.model.project.Schedule;
+
+/**
+ * This is not an entity.
+ *
+ * @version $Revision$ $Date$
+ */
+public class ContinuumDatabase implements Serializable
+{
+
+ /**
+ * Field projectGroups
+ */
+ private java.util.List projectGroups;
+
+ /**
+ * Field systemConfiguration
+ */
+ private SystemConfiguration systemConfiguration;
+
+ /**
+ * Field installations
+ */
+ private java.util.List installations;
+
+ /**
+ * Field schedules
+ */
+ private java.util.List schedules;
+
+ /**
+ * Field profiles
+ */
+ private java.util.List profiles;
+
+ // -----------/
+ // - Methods -/
+ // -----------/
+
+ /**
+ * Method addInstallation
+ *
+ * @param installation
+ */
+ public void addInstallation( Installation installation )
+ {
+ if ( !( installation instanceof Installation ) )
+ {
+ throw new ClassCastException(
+ "ContinuumDatabase.addInstallations(installation) parameter must be instanceof "
+ + Installation.class.getName() );
+ }
+ getInstallations().add( installation );
+ } // -- void addInstallation(Installation)
+
+ /**
+ * Method addProfile
+ *
+ * @param profile
+ */
+ public void addProfile( Profile profile )
+ {
+ if ( !( profile instanceof Profile ) )
+ {
+ throw new ClassCastException( "ContinuumDatabase.addProfiles(profile) parameter must be instanceof "
+ + Profile.class.getName() );
+ }
+ getProfiles().add( profile );
+ } // -- void addProfile(Profile)
+
+ /**
+ * Method addProjectGroup
+ *
+ * @param projectGroup
+ */
+ public void addProjectGroup( ProjectGroup projectGroup )
+ {
+ if ( !( projectGroup instanceof ProjectGroup ) )
+ {
+ throw new ClassCastException(
+ "ContinuumDatabase.addProjectGroups(projectGroup) parameter must be instanceof "
+ + ProjectGroup.class.getName() );
+ }
+ getProjectGroups().add( projectGroup );
+ } // -- void addProjectGroup(ProjectGroup)
+
+ /**
+ * Method addSchedule
+ *
+ * @param schedule
+ */
+ public void addSchedule( Schedule schedule )
+ {
+ if ( !( schedule instanceof Schedule ) )
+ {
+ throw new ClassCastException( "ContinuumDatabase.addSchedules(schedule) parameter must be instanceof "
+ + Schedule.class.getName() );
+ }
+ getSchedules().add( schedule );
+ } // -- void addSchedule(Schedule)
+
+ /**
+ * Method getInstallations
+ */
+ public java.util.List getInstallations()
+ {
+ if ( this.installations == null )
+ {
+ this.installations = new java.util.ArrayList();
+ }
+
+ return this.installations;
+ } // -- java.util.List getInstallations()
+
+ /**
+ * Method getProfiles
+ */
+ public java.util.List getProfiles()
+ {
+ if ( this.profiles == null )
+ {
+ this.profiles = new java.util.ArrayList();
+ }
+
+ return this.profiles;
+ } // -- java.util.List getProfiles()
+
+ /**
+ * Method getProjectGroups
+ */
+ public java.util.List getProjectGroups()
+ {
+ if ( this.projectGroups == null )
+ {
+ this.projectGroups = new java.util.ArrayList();
+ }
+
+ return this.projectGroups;
+ } // -- java.util.List getProjectGroups()
+
+ /**
+ * Method getSchedules
+ */
+ public java.util.List getSchedules()
+ {
+ if ( this.schedules == null )
+ {
+ this.schedules = new java.util.ArrayList();
+ }
+
+ return this.schedules;
+ } // -- java.util.List getSchedules()
+
+ /**
+ * Get null
+ */
+ public SystemConfiguration getSystemConfiguration()
+ {
+ return this.systemConfiguration;
+ } // -- SystemConfiguration getSystemConfiguration()
+
+ /**
+ * Method removeInstallation
+ *
+ * @param installation
+ */
+ public void removeInstallation( Installation installation )
+ {
+ if ( !( installation instanceof Installation ) )
+ {
+ throw new ClassCastException(
+ "ContinuumDatabase.removeInstallations(installation) parameter must be instanceof "
+ + Installation.class.getName() );
+ }
+ getInstallations().remove( installation );
+ } // -- void removeInstallation(Installation)
+
+ /**
+ * Method removeProfile
+ *
+ * @param profile
+ */
+ public void removeProfile( Profile profile )
+ {
+ if ( !( profile instanceof Profile ) )
+ {
+ throw new ClassCastException( "ContinuumDatabase.removeProfiles(profile) parameter must be instanceof "
+ + Profile.class.getName() );
+ }
+ getProfiles().remove( profile );
+ } // -- void removeProfile(Profile)
+
+ /**
+ * Method removeProjectGroup
+ *
+ * @param projectGroup
+ */
+ public void removeProjectGroup( ProjectGroup projectGroup )
+ {
+ if ( !( projectGroup instanceof ProjectGroup ) )
+ {
+ throw new ClassCastException(
+ "ContinuumDatabase.removeProjectGroups(projectGroup) parameter must be instanceof "
+ + ProjectGroup.class.getName() );
+ }
+ getProjectGroups().remove( projectGroup );
+ } // -- void removeProjectGroup(ProjectGroup)
+
+ /**
+ * Method removeSchedule
+ *
+ * @param schedule
+ */
+ public void removeSchedule( Schedule schedule )
+ {
+ if ( !( schedule instanceof Schedule ) )
+ {
+ throw new ClassCastException( "ContinuumDatabase.removeSchedules(schedule) parameter must be instanceof "
+ + Schedule.class.getName() );
+ }
+ getSchedules().remove( schedule );
+ } // -- void removeSchedule(Schedule)
+
+ /**
+ * Set null
+ *
+ * @param installations
+ */
+ public void setInstallations( java.util.List installations )
+ {
+ this.installations = installations;
+ } // -- void setInstallations(java.util.List)
+
+ /**
+ * Set null
+ *
+ * @param profiles
+ */
+ public void setProfiles( java.util.List profiles )
+ {
+ this.profiles = profiles;
+ } // -- void setProfiles(java.util.List)
+
+ /**
+ * Set null
+ *
+ * @param projectGroups
+ */
+ public void setProjectGroups( java.util.List projectGroups )
+ {
+ this.projectGroups = projectGroups;
+ } // -- void setProjectGroups(java.util.List)
+
+ /**
+ * Set null
+ *
+ * @param schedules
+ */
+ public void setSchedules( java.util.List schedules )
+ {
+ this.schedules = schedules;
+ } // -- void setSchedules(java.util.List)
+
+ /**
+ * Set null
+ *
+ * @param systemConfiguration
+ */
+ public void setSystemConfiguration( SystemConfiguration systemConfiguration )
+ {
+ this.systemConfiguration = systemConfiguration;
+ } // -- void setSystemConfiguration(SystemConfiguration)
+
+ private String modelEncoding = "UTF-8";
+
+ public void setModelEncoding( String modelEncoding )
+ {
+ this.modelEncoding = modelEncoding;
+ }
+
+ public String getModelEncoding()
+ {
+ return modelEncoding;
+ }
+}
diff --git a/src/main/java/org/apache/continuum/model/system/Installation.java b/src/main/java/org/apache/continuum/model/system/Installation.java
new file mode 100644
index 0000000..14a995b
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/system/Installation.java
@@ -0,0 +1,114 @@
+package org.apache.continuum.model.system;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "INSTALLATION")
+public class Installation
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field type
+ * <p/>
+ * TODO: Enum?
+ */
+ @Basic
+ @Column(name = "INSTALLATION_TYPE")
+ private String type;
+
+ /**
+ * Field varValue
+ */
+ @Basic
+ @Column(name = "VAR_VALUE")
+ private String varValue;
+
+ /**
+ * Field varName
+ */
+ @Basic
+ @Column(name = "VAR_NAME")
+ private String varName;
+
+ /**
+ * Field name
+ */
+ @Basic
+ @Column(name = "NAME")
+ private String name;
+
+ /**
+ * @return the type
+ */
+ public String getType()
+ {
+ return type;
+ }
+
+ /**
+ * @param type the type to set
+ */
+ public void setType( String type )
+ {
+ this.type = type;
+ }
+
+ /**
+ * @return the varValue
+ */
+ public String getVarValue()
+ {
+ return varValue;
+ }
+
+ /**
+ * @param varValue the varValue to set
+ */
+ public void setVarValue( String varValue )
+ {
+ this.varValue = varValue;
+ }
+
+ /**
+ * @return the varName
+ */
+ public String getVarName()
+ {
+ return varName;
+ }
+
+ /**
+ * @param varName the varName to set
+ */
+ public void setVarName( String varName )
+ {
+ this.varName = varName;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/system/NotificationAddress.java b/src/main/java/org/apache/continuum/model/system/NotificationAddress.java
new file mode 100644
index 0000000..84067b6
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/system/NotificationAddress.java
@@ -0,0 +1,94 @@
+package org.apache.continuum.model.system;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+/**
+ * Configures one method for notifying users/developers when a build breaks.
+ *
+ * @version $Id$
+ */
+@Entity
+@Table(name = "NOTIFICATION_ADDRESS")
+public class NotificationAddress
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field type
+ * <p/>
+ * TODO: Enum?
+ */
+ @Basic
+ @Column(name = "ADDRESS_TYPE")
+ private String type = "mail";
+
+ /**
+ * Field address
+ */
+ @Basic
+ @Column(name = "ADDRESS")
+ private String address;
+
+ /**
+ * Field configuration
+ * <p/>
+ * TODO: Map!
+ */
+ @Transient
+ private java.util.Map configuration;
+
+ /**
+ * @return the type
+ */
+ public String getType()
+ {
+ return type;
+ }
+
+ /**
+ * @param type the type to set
+ */
+ public void setType( String type )
+ {
+ this.type = type;
+ }
+
+ /**
+ * @return the address
+ */
+ public String getAddress()
+ {
+ return address;
+ }
+
+ /**
+ * @param address the address to set
+ */
+ public void setAddress( String address )
+ {
+ this.address = address;
+ }
+
+ /**
+ * @return the configuration
+ */
+ public java.util.Map getConfiguration()
+ {
+ return configuration;
+ }
+
+ /**
+ * @param configuration the configuration to set
+ */
+ public void setConfiguration( java.util.Map configuration )
+ {
+ this.configuration = configuration;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/system/Profile.java b/src/main/java/org/apache/continuum/model/system/Profile.java
new file mode 100644
index 0000000..b54c75b
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/system/Profile.java
@@ -0,0 +1,206 @@
+package org.apache.continuum.model.system;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import java.util.List;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "PROFILE")
+public class Profile
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field active
+ */
+ @Basic
+ @Column(name = "FLG_ACTIVE", nullable = false)
+ private boolean active = false;
+
+ /**
+ * Field name
+ */
+ @Basic
+ @Column(name = "NAME", nullable = false)
+ private String name;
+
+ /**
+ * Field description
+ */
+ @Basic
+ @Column(name = "DESCRIPTION")
+ private String description;
+
+ /**
+ * Field scmMode
+ * <p/>
+ * TODO: Enum?
+ */
+ @Basic
+ @Column(name = "SCM_MODE", nullable = false)
+ private int scmMode = 0;
+
+ /**
+ * Field buildWithoutChanges
+ */
+ @Basic
+ @Column(name = "FLG_BUILD_WITHOUT_CHANGES", nullable = false)
+ private boolean buildWithoutChanges = false;
+
+ /**
+ * Field jdk
+ */
+ @OneToOne
+ private Installation jdk;
+
+ /**
+ * Field builder
+ */
+ @OneToOne
+ private Installation builder;
+
+ /**
+ * Field environmentVariables
+ */
+ @OneToMany
+ private List<Installation> environmentVariables;
+
+ /**
+ * @return the active
+ */
+ public boolean isActive()
+ {
+ return active;
+ }
+
+ /**
+ * @param active the active to set
+ */
+ public void setActive( boolean active )
+ {
+ this.active = active;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return the description
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @param description the description to set
+ */
+ public void setDescription( String description )
+ {
+ this.description = description;
+ }
+
+ /**
+ * @return the scmMode
+ */
+ public int getScmMode()
+ {
+ return scmMode;
+ }
+
+ /**
+ * @param scmMode the scmMode to set
+ */
+ public void setScmMode( int scmMode )
+ {
+ this.scmMode = scmMode;
+ }
+
+ /**
+ * @return the buildWithoutChanges
+ */
+ public boolean isBuildWithoutChanges()
+ {
+ return buildWithoutChanges;
+ }
+
+ /**
+ * @param buildWithoutChanges the buildWithoutChanges to set
+ */
+ public void setBuildWithoutChanges( boolean buildWithoutChanges )
+ {
+ this.buildWithoutChanges = buildWithoutChanges;
+ }
+
+ /**
+ * @return the jdk
+ */
+ public Installation getJdk()
+ {
+ return jdk;
+ }
+
+ /**
+ * @param jdk the jdk to set
+ */
+ public void setJdk( Installation jdk )
+ {
+ this.jdk = jdk;
+ }
+
+ /**
+ * @return the builder
+ */
+ public Installation getBuilder()
+ {
+ return builder;
+ }
+
+ /**
+ * @param builder the builder to set
+ */
+ public void setBuilder( Installation builder )
+ {
+ this.builder = builder;
+ }
+
+ /**
+ * @return the environmentVariables
+ */
+ public List<Installation> getEnvironmentVariables()
+ {
+ return environmentVariables;
+ }
+
+ /**
+ * @param environmentVariables the environmentVariables to set
+ */
+ public void setEnvironmentVariables( List<Installation> environmentVariables )
+ {
+ this.environmentVariables = environmentVariables;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/model/system/SystemConfiguration.java b/src/main/java/org/apache/continuum/model/system/SystemConfiguration.java
new file mode 100644
index 0000000..d1015ef
--- /dev/null
+++ b/src/main/java/org/apache/continuum/model/system/SystemConfiguration.java
@@ -0,0 +1,204 @@
+package org.apache.continuum.model.system;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ */
+@Entity
+@Table(name = "SYSTEM_CONFIGURATION")
+public class SystemConfiguration
+ extends CommonUpdatableEntity
+{
+
+ /**
+ * Field guestAccountEnabled
+ */
+ @Basic
+ @Column(name = "FLG_GUEST_ACCOUNT_ENABLED", nullable = false)
+ private boolean guestAccountEnabled = true;
+
+ /**
+ * Field defaultScheduleDescription
+ */
+ @Basic
+ @Column(name = "DEFAULT_SCHEDULE_DESC", nullable = false)
+ private String defaultScheduleDescription = "Run hourly";
+
+ /**
+ * Field defaultScheduleCronExpression
+ */
+ @Basic
+ @Column(name = "DEFAULT_SCHEDULE_CRON_EXP", nullable = false)
+ private String defaultScheduleCronExpression = "0 0 * * * ?";
+
+ /**
+ * Field workingDirectory
+ */
+ @Basic
+ @Column(name = "WORKING_DIRECTORY", nullable = false)
+ private String workingDirectory = "working-directory";
+
+ /**
+ * Field buildOutputDirectory
+ */
+ @Basic
+ @Column(name = "BUILD_OUTPUT_DIRECTORY", nullable = false)
+ private String buildOutputDirectory = "build-output-directory";
+
+ /**
+ * Field deploymentRepositoryDirectory
+ */
+ @Basic
+ @Column(name = "DEPLOYMENT_REPOSITORY_DIRECTORY")
+ private String deploymentRepositoryDirectory;
+
+ /**
+ * Field baseUrl
+ */
+ @Basic
+ @Column(name = "BASE_URL")
+ private String baseUrl;
+
+ /**
+ * Field initialized
+ */
+ @Basic
+ @Column(name = "FLG_INITIALIZED", nullable = false)
+ private boolean initialized = false;
+
+ /**
+ * @return the guestAccountEnabled
+ */
+ public boolean isGuestAccountEnabled()
+ {
+ return guestAccountEnabled;
+ }
+
+ /**
+ * @param guestAccountEnabled the guestAccountEnabled to set
+ */
+ public void setGuestAccountEnabled( boolean guestAccountEnabled )
+ {
+ this.guestAccountEnabled = guestAccountEnabled;
+ }
+
+ /**
+ * @return the defaultScheduleDescription
+ */
+ public String getDefaultScheduleDescription()
+ {
+ return defaultScheduleDescription;
+ }
+
+ /**
+ * @param defaultScheduleDescription the defaultScheduleDescription to set
+ */
+ public void setDefaultScheduleDescription( String defaultScheduleDescription )
+ {
+ this.defaultScheduleDescription = defaultScheduleDescription;
+ }
+
+ /**
+ * @return the defaultScheduleCronExpression
+ */
+ public String getDefaultScheduleCronExpression()
+ {
+ return defaultScheduleCronExpression;
+ }
+
+ /**
+ * @param defaultScheduleCronExpression the defaultScheduleCronExpression to set
+ */
+ public void setDefaultScheduleCronExpression( String defaultScheduleCronExpression )
+ {
+ this.defaultScheduleCronExpression = defaultScheduleCronExpression;
+ }
+
+ /**
+ * @return the workingDirectory
+ */
+ public String getWorkingDirectory()
+ {
+ return workingDirectory;
+ }
+
+ /**
+ * @param workingDirectory the workingDirectory to set
+ */
+ public void setWorkingDirectory( String workingDirectory )
+ {
+ this.workingDirectory = workingDirectory;
+ }
+
+ /**
+ * @return the buildOutputDirectory
+ */
+ public String getBuildOutputDirectory()
+ {
+ return buildOutputDirectory;
+ }
+
+ /**
+ * @param buildOutputDirectory the buildOutputDirectory to set
+ */
+ public void setBuildOutputDirectory( String buildOutputDirectory )
+ {
+ this.buildOutputDirectory = buildOutputDirectory;
+ }
+
+ /**
+ * @return the deploymentRepositoryDirectory
+ */
+ public String getDeploymentRepositoryDirectory()
+ {
+ return deploymentRepositoryDirectory;
+ }
+
+ /**
+ * @param deploymentRepositoryDirectory the deploymentRepositoryDirectory to set
+ */
+ public void setDeploymentRepositoryDirectory( String deploymentRepositoryDirectory )
+ {
+ this.deploymentRepositoryDirectory = deploymentRepositoryDirectory;
+ }
+
+ /**
+ * @return the baseUrl
+ */
+ public String getBaseUrl()
+ {
+ return baseUrl;
+ }
+
+ /**
+ * @param baseUrl the baseUrl to set
+ */
+ public void setBaseUrl( String baseUrl )
+ {
+ this.baseUrl = baseUrl;
+ }
+
+ /**
+ * @return the initialized
+ */
+ public boolean isInitialized()
+ {
+ return initialized;
+ }
+
+ /**
+ * @param initialized the initialized to set
+ */
+ public void setInitialized( boolean initialized )
+ {
+ this.initialized = initialized;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/service/api/ProjectGroupService.java b/src/main/java/org/apache/continuum/service/api/ProjectGroupService.java
new file mode 100644
index 0000000..9231aa9
--- /dev/null
+++ b/src/main/java/org/apache/continuum/service/api/ProjectGroupService.java
@@ -0,0 +1,35 @@
+package org.apache.continuum.service.api;
+
+import org.apache.continuum.model.project.Project;
+import org.apache.continuum.model.project.ProjectGroup;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
+ * @version $Id$
+ */
+public interface ProjectGroupService
+{
+ ProjectGroup saveOrUpdate( ProjectGroup projectGroup );
+
+ ProjectGroup getProjectGroup( long pgId );
+
+ ProjectGroup getProjectGroup( String groupId );
+
+ ProjectGroup addProjectGroup( ProjectGroup pg );
+
+ void removeProjectGroup( ProjectGroup pg );
+
+ void addProject( ProjectGroup pg, Project p );
+
+ void removeProject( ProjectGroup pg, Project p );
+
+ List<Project> getProjects( ProjectGroup pg );
+
+ //BuildResult buildProjectGroup( ProjectGroup pg );
+
+ //BuildResult buildProjectGroup( ProjectGroup, BuildDefinition bd );
+
+ //BuildResult buildProjectGroup( ProjectGroup, BuildDefinition bd, boolean force );
+}
diff --git a/src/main/java/org/apache/continuum/service/api/ProjectService.java b/src/main/java/org/apache/continuum/service/api/ProjectService.java
new file mode 100644
index 0000000..9c179fa
--- /dev/null
+++ b/src/main/java/org/apache/continuum/service/api/ProjectService.java
@@ -0,0 +1,33 @@
+package org.apache.continuum.service.api;
+
+import org.apache.continuum.model.project.Project;
+import org.apache.continuum.model.project.ProjectNotifier;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
+ * @version $Id$
+ */
+public interface ProjectService
+{
+ Project saveOrUpdate( Project project );
+
+ Project getProject( long projecId );
+
+ Project getProject( String groupId, String artifactId, String version );
+
+ List<ProjectNotifier> getNotifiers( Project p );
+
+ void addNotifier( Project p, ProjectNotifier notifier );
+
+ void removeNotifier( Project p, ProjectNotifier notifier );
+
+ //BuildDefinition getDefaultBuildDefinition( Project p );
+
+ //BuildResult buildProject( Project p );
+
+ //BuildResult buildProject( Project p, BuildDefinition bd );
+
+ //BuildResult buildProject( Project p, BuildDefinition bd, boolean force );
+}
diff --git a/src/main/java/org/apache/continuum/service/impl/ProjectGroupServiceImpl.java b/src/main/java/org/apache/continuum/service/impl/ProjectGroupServiceImpl.java
new file mode 100644
index 0000000..d419336
--- /dev/null
+++ b/src/main/java/org/apache/continuum/service/impl/ProjectGroupServiceImpl.java
@@ -0,0 +1,105 @@
+package org.apache.continuum.service.impl;
+
+import org.apache.continuum.dao.api.GenericDao;
+import org.apache.continuum.model.project.Project;
+import org.apache.continuum.model.project.ProjectGroup;
+import org.apache.continuum.service.api.ProjectGroupService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
+ * @version $Id$
+ */
+@Service
+public class ProjectGroupServiceImpl
+ implements ProjectGroupService
+{
+ GenericDao<ProjectGroup> projectGroupDao;
+
+ GenericDao<Project> projectDao;
+
+ public ProjectGroup saveOrUpdate( ProjectGroup projectGroup )
+ {
+ return projectGroupDao.saveOrUpdate( projectGroup );
+ }
+
+ public ProjectGroup getProjectGroup( long pgId )
+ {
+ return projectGroupDao.findById( pgId );
+ }
+
+ public ProjectGroup getProjectGroup( String groupId )
+ {
+ Map<String, Object> params = new HashMap<String, Object>();
+ params.put( "groupId", groupId );
+ return projectGroupDao.findUniqByNamedQueryAndNamedParams( ProjectGroup.class, "ProjectGroup.findProjectGroup",
+ params );
+ }
+
+ public ProjectGroup addProjectGroup( ProjectGroup pg )
+ {
+ return projectGroupDao.saveOrUpdate( pg );
+ }
+
+ @Transactional
+ public void removeProjectGroup( ProjectGroup pg )
+ {
+ if ( pg == null )
+ {
+ return;
+ }
+
+ for ( Project p : pg.getProjects() )
+ {
+ projectDao.delete( p );
+ }
+
+ projectGroupDao.delete( pg );
+ }
+
+ public void addProject( ProjectGroup pg, Project p )
+ {
+ pg.addProject( p );
+ projectGroupDao.saveOrUpdate( pg );
+ }
+
+ @Transactional
+ public void removeProject( ProjectGroup pg, Project p )
+ {
+ pg.removeProject( p );
+ projectDao.delete( p );
+ projectGroupDao.saveOrUpdate( pg );
+ }
+
+ public List<Project> getProjects( ProjectGroup pg )
+ {
+ Map<String, Object> params = new HashMap<String, Object>();
+ params.put( "projectGroup", pg );
+ return projectDao.findByNamedQueryAndNamedParams( Project.class, "ProjectGroup.findProjects", params );
+ }
+
+ public GenericDao<ProjectGroup> getProjectGroupDao()
+ {
+ return projectGroupDao;
+ }
+
+ public void setProjectGroupDao( GenericDao<ProjectGroup> projectGroupDao )
+ {
+ this.projectGroupDao = projectGroupDao;
+ }
+
+ public GenericDao<Project> getProjectDao()
+ {
+ return projectDao;
+ }
+
+ public void setProjectDao( GenericDao<Project> projectDao )
+ {
+ this.projectDao = projectDao;
+ }
+}
diff --git a/src/main/java/org/apache/continuum/service/impl/ProjectServiceImpl.java b/src/main/java/org/apache/continuum/service/impl/ProjectServiceImpl.java
new file mode 100644
index 0000000..a0b0c07
--- /dev/null
+++ b/src/main/java/org/apache/continuum/service/impl/ProjectServiceImpl.java
@@ -0,0 +1,85 @@
+package org.apache.continuum.service.impl;
+
+import org.apache.continuum.dao.api.GenericDao;
+import org.apache.continuum.model.project.Project;
+import org.apache.continuum.model.project.ProjectNotifier;
+import org.apache.continuum.service.api.ProjectService;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
+ * @version $Id$
+ */
+public class ProjectServiceImpl
+ implements ProjectService
+{
+ GenericDao<Project> projectDao;
+
+ GenericDao<ProjectNotifier> notifierDao;
+
+ public Project saveOrUpdate( Project project )
+ {
+ return projectDao.saveOrUpdate( project );
+ }
+
+ public Project getProject( long projecId )
+ {
+ return projectDao.findById( projecId );
+ }
+
+ public Project getProject( String groupId, String artifactId, String version )
+ {
+ Map<String, Object> params = new HashMap<String, Object>();
+ params.put( "groupId", groupId );
+ params.put( "artifactId", artifactId );
+ params.put( "version", version );
+ return projectDao.findUniqByNamedQueryAndNamedParams( Project.class, "Project.find", params );
+ }
+
+ public List<ProjectNotifier> getNotifiers( Project p )
+ {
+ Map<String, Object> params = new HashMap<String, Object>();
+ params.put( "projectId", p.getId() );
+ return notifierDao.findByNamedQueryAndNamedParams( ProjectNotifier.class, "Notifier.findAllFromProject",
+ params );
+ }
+
+ @Transactional
+ public void addNotifier( Project p, ProjectNotifier notifier )
+ {
+ p.addNotifier( notifier );
+ projectDao.saveOrUpdate( p );
+ }
+
+ @Transactional
+ public void removeNotifier( Project p, ProjectNotifier notifier )
+ {
+ p.removeNotifier( notifier );
+ notifierDao.delete( notifier );
+ projectDao.saveOrUpdate( p );
+ }
+
+ public GenericDao<Project> getProjectDao()
+ {
+ return projectDao;
+ }
+
+ public void setProjectDao( GenericDao<Project> projectDao )
+ {
+ this.projectDao = projectDao;
+ }
+
+ public GenericDao<ProjectNotifier> getNotifierDao()
+ {
+ return notifierDao;
+ }
+
+ public void setNotifierDao( GenericDao<ProjectNotifier> notifierDao )
+ {
+ this.notifierDao = notifierDao;
+ }
+}
diff --git a/src/main/java/org/apache/continuum/store/api/DeprecatedSystemStore.java b/src/main/java/org/apache/continuum/store/api/DeprecatedSystemStore.java
new file mode 100644
index 0000000..d1df57e
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/api/DeprecatedSystemStore.java
@@ -0,0 +1,207 @@
+package org.apache.continuum.store.api;
+
+import java.util.List;
+
+import org.apache.continuum.model.project.Schedule;
+import org.apache.continuum.model.system.Installation;
+import org.apache.continuum.model.system.Profile;
+import org.apache.continuum.model.system.SystemConfiguration;
+
+/**
+ * Defines the contract consisting of operations that can be performed on following entities:
+ * <ul>
+ * <li>{@link Schedule},</li>
+ * <li>{@link Profile},</li>
+ * <li>{@link Installation},</li>
+ * <li>{@link SystemConfiguration}</li>
+ * </ul>
+ *
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ * @since 1.1
+ */
+public interface DeprecatedSystemStore
+{
+ public static final String ROLE = DeprecatedSystemStore.class.getName();
+
+ /**
+ * Removes the passed {@link Installation} instance from the underlying store.
+ *
+ * @param project
+ * {@link Installation} instance to remove.
+ * @throws StoreException
+ * if there was an error removing the entity.
+ */
+ public void deleteInstallation( Installation installation ) throws StoreException;
+
+ /**
+ * Removes the passed {@link Profile} instance from the underlying store.
+ *
+ * @param project
+ * {@link Profile} instance to remove.
+ * @throws StoreException
+ * if there was an error removing the entity.
+ */
+ public void deleteProfile( Profile profile ) throws StoreException;
+
+ /**
+ * Removes the passed {@link Schedule} instance from the underlying store.
+ *
+ * @param project
+ * {@link Schedule} instance to remove.
+ * @throws StoreException
+ * if there was an error removing the entity.
+ */
+ public void deleteSchedule( Schedule schedule ) throws StoreException;
+
+ /**
+ * Removes the passed {@link SystemConfiguration} instance from the underlying store.
+ *
+ * @param project
+ * {@link SystemConfiguration} instance to remove.
+ * @throws StoreException
+ * if there was an error removing the entity.
+ */
+ public void deleteSystemConfiguration( SystemConfiguration systemConfiguration ) throws StoreException;
+
+ /**
+ * Looks up the underlying store and returns a {@link Installation} instance that matches the specified id.
+ *
+ * @param id
+ * {@link Installation} id to match.
+ * @return matching {@link Installation} instance.
+ * @throws EntityNotFoundException
+ * if the instance could not be looked up.
+ * @throws StoreException
+ */
+ public Installation lookupInstallation( long id ) throws EntityNotFoundException, StoreException;
+
+ /**
+ * Looks up the underlying store and returns a {@link Profile} instance that matches the specified id.
+ *
+ * @param id
+ * {@link Profile} id to match.
+ * @return matching {@link Profile} instance.
+ * @throws EntityNotFoundException
+ * if the instance could not be looked up.
+ * @throws StoreException
+ */
+ public Profile lookupProfile( long id ) throws EntityNotFoundException, StoreException;
+
+ /**
+ * Looks up the underlying store and returns a {@link Schedule} instance that matches the specified id.
+ *
+ * @param id
+ * {@link Schedule} id to match.
+ * @return matching {@link Schedule} instance.
+ * @throws EntityNotFoundException
+ * if the instance could not be looked up.
+ * @throws StoreException
+ */
+ public Schedule lookupSchedule( long id ) throws EntityNotFoundException, StoreException;
+
+ /**
+ * Looks up the underlying store and returns a {@link SystemConfiguration} instance that matches the specified id.
+ *
+ * @param id
+ * {@link SystemConfiguration} id to match.
+ * @return matching {@link SystemConfiguration} instance.
+ * @throws EntityNotFoundException
+ * if the instance could not be looked up.
+ * @throws StoreException
+ */
+ public SystemConfiguration lookupSystemConfiguration( long id ) throws EntityNotFoundException, StoreException;
+
+ /**
+ * Persists the passed in {@link Installation} instance to the underlying store.
+ * <p>
+ * If the entity instance already exists in the database it is updated, else a new instance is created and an
+ * store-generated identifier assigned to it.
+ *
+ * @param project
+ * {@link Installation} instance to be created/saved.
+ * @return updated {@link Installation} instance.
+ * @throws StoreException
+ * if there was an error saving the entity.
+ */
+ public Installation saveInstallation( Installation installation ) throws StoreException;
+
+ /**
+ * Persists the passed in {@link Profile} instance to the underlying store.
+ * <p>
+ * If the entity instance already exists in the database it is updated, else a new instance is created and an
+ * store-generated identifier assigned to it.
+ *
+ * @param project
+ * {@link Profile} instance to be created/saved.
+ * @return updated {@link Profile} instance.
+ * @throws StoreException
+ * if there was an error saving the entity.
+ */
+ public Profile saveProfile( Profile profile ) throws StoreException;
+
+ /**
+ * Persists the passed in {@link Schedule} instance to the underlying store.
+ * <p>
+ * If the entity instance already exists in the database it is updated, else a new instance is created and an
+ * store-generated identifier assigned to it.
+ *
+ * @param project
+ * {@link Schedule} instance to be created/saved.
+ * @return updated {@link Schedule} instance.
+ * @throws StoreException
+ * if there was an error saving the entity.
+ */
+ public Schedule saveSchedule( Schedule schedule ) throws StoreException;
+
+ /**
+ * Persists the passed in {@link SystemConfiguration} instance to the underlying store.
+ * <p>
+ * If the entity instance already exists in the database it is updated, else a new instance is created and an
+ * store-generated identifier assigned to it.
+ *
+ * @param project
+ * {@link SystemConfiguration} instance to be created/saved.
+ * @return updated {@link SystemConfiguration} instance.
+ * @throws StoreException
+ * if there was an error saving the entity.
+ */
+ public SystemConfiguration saveSystemConfiguration( SystemConfiguration systemConfiguration ) throws StoreException;
+
+ /**
+ * Obtains and returns a {@link List} of <b>all</b> {@link Schedule} instances for the system, stored in the
+ * underlying store.
+ *
+ * @return list of all {@link Schedule} instances stored.
+ * @throws StoreException
+ */
+ public List getAllSchedules() throws StoreException;
+
+ /**
+ * Obtains and returns a {@link List} of <b>all</b> {@link Profile} instances for the system, stored in the
+ * underlying store.
+ *
+ * @return list of all {@link Profile} instances stored.
+ * @throws StoreException
+ */
+ public List getAllProfiles() throws StoreException;
+
+ /**
+ * Obtains and returns a {@link List} of <b>all</b> {@link Installation} instances for the system, stored in the
+ * underlying store.
+ *
+ * @return list of all {@link Installation} instances stored.
+ * @throws StoreException
+ */
+ public List getAllInstallations() throws StoreException;
+
+ /**
+ * Obtains and returns a {@link List} of <b>all</b> {@link SystemConfiguration} instances for the system, stored in
+ * the underlying store.
+ *
+ * @return list of all {@link SystemConfiguration} instances stored.
+ * @throws StoreException
+ */
+ public List getAllSystemConfigurations() throws StoreException;
+
+}
diff --git a/src/main/java/org/apache/continuum/store/api/EntityNotFoundException.java b/src/main/java/org/apache/continuum/store/api/EntityNotFoundException.java
new file mode 100644
index 0000000..7eff923
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/api/EntityNotFoundException.java
@@ -0,0 +1,42 @@
+package org.apache.continuum.store.api;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author <a href="mailto:trygvis@inamo.no">Trygve Laugstøl</a>
+ * @version $Id$
+ */
+public class EntityNotFoundException extends StoreException
+{
+ public EntityNotFoundException()
+ {
+ super();
+ }
+
+ public EntityNotFoundException( String message )
+ {
+ super( message );
+ }
+
+ public EntityNotFoundException( String type, String id )
+ {
+ this( "Could not find object. Type '" + type + "'. Id: '" + id + "'." );
+ }
+}
diff --git a/src/main/java/org/apache/continuum/store/api/Query.java b/src/main/java/org/apache/continuum/store/api/Query.java
new file mode 100644
index 0000000..d4e1eb5
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/api/Query.java
@@ -0,0 +1,33 @@
+/**
+ *
+ */
+package org.apache.continuum.store.api;
+
+import java.util.Map;
+
+/**
+ * Wraps up Type Query criteria to be used by store extensions to filter (and obtain) matching type instances.
+ * <p>
+ * Implementations/extensions are expected to override {@link Object#toString()} method and return a <b>JPQL</b>
+ * formatted string. The JPQL string is consumed by the {@link Store} implementation in {@link Store#query(Query)}
+ * operations.
+ *
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ * @since 1.2
+ */
+public interface Query<Q>
+{
+
+ /**
+ * Returns this instance of {@link Query} as a JPQL String.
+ *
+ * @param whereClause
+ * {@link Map} containing the named parameters to be substituted in the JPQL query. This is populated by
+ * the {@link Query} implementation and subsequently used by the {@link Store} implementation to
+ * interpolate the parameters before the JPQL query is executed.
+ *
+ * @return {@link Query} as a JPQL String
+ */
+ public String toString( Map<String, Object> whereClause );
+}
diff --git a/src/main/java/org/apache/continuum/store/api/QueryFactory.java b/src/main/java/org/apache/continuum/store/api/QueryFactory.java
new file mode 100644
index 0000000..bcd8c3c
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/api/QueryFactory.java
@@ -0,0 +1,32 @@
+/**
+ *
+ */
+package org.apache.continuum.store.api;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ * @since 1.2
+ */
+public class QueryFactory
+{
+ public static <T, Q extends Query<T>> Q createQuery( Class<Q> klass )
+ {
+ Q qry = null;
+ try
+ {
+ qry = klass.newInstance();
+ }
+ catch ( InstantiationException e )
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ catch ( IllegalAccessException e )
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return qry;
+ }
+}
diff --git a/src/main/java/org/apache/continuum/store/api/Store.java b/src/main/java/org/apache/continuum/store/api/Store.java
new file mode 100644
index 0000000..c74453d
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/api/Store.java
@@ -0,0 +1,79 @@
+/**
+ *
+ */
+package org.apache.continuum.store.api;
+
+import java.util.List;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+
+/**
+ * Interface that Continuum store extensions/implementations are expected to implement to allow operations on the
+ * underlying store.
+ * <ul>
+ * <li>Entity look ups</li>
+ * <li>Entity insert/updates</li>
+ * <li>Entity removal</li>
+ * <li>Querying one or more entity/entities based on specified criteria</li>
+ * </ul>
+ *
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ * @since 1.2
+ */
+public interface Store<T extends CommonUpdatableEntity, Q extends Query<T>>
+{
+
+ /**
+ * Looks up the underlying store and returns a {@link T} instance that matches the specified id.
+ *
+ * @param klass
+ * {@link Class} for type entity to lookup and return an instance of.
+ * @param id
+ * Entity Type {@link T}'s id to match.
+ *
+ * @return matching entity type {@link T} instance.
+ * @throws StoreException
+ * @throws EntityNotFoundException
+ * if the entity specified by the identifier could be located in the system.
+ * @throws EntityNotFoundException
+ * if the instance could not be looked up.
+ */
+ public T lookup( Class<T> klass, Long id ) throws StoreException, EntityNotFoundException;
+
+ /**
+ * Persists the passed in entity type {@link T} instance to the underlying store.
+ * <p>
+ * If the entity instance already exists in the database it is updated, else a new instance is created and an
+ * store-generated identifier assigned to it.
+ *
+ * @param entity
+ * Type {@link T} instance to be created/saved.
+ * @return updated entity type {@link T} instance.
+ * @throws StoreException
+ * if there was an error saving the entity.
+ */
+ public T save( T entity ) throws StoreException;
+
+ /**
+ * Removes the passed entity type {@link T} instance from the underlying store.
+ *
+ * @param entity
+ * Type {@link T} instance to remove.
+ * @throws StoreException
+ * if there was an error removing the entity.
+ */
+ public void delete( T entity ) throws StoreException;
+
+ /**
+ * Obtains a {@link List} of instances of entity type {@link T} which match the criteria specified by the passed in
+ * query instance.
+ *
+ * @param query
+ * instance that wraps up the criteria for querying matching instances in the system.
+ * @return {@link List} of instances of entity type {@link T} which match the specified query.
+ * @throws StoreException
+ */
+ public List<T> query( Q query ) throws StoreException;
+
+}
diff --git a/src/main/java/org/apache/continuum/store/api/StoreException.java b/src/main/java/org/apache/continuum/store/api/StoreException.java
new file mode 100644
index 0000000..e67ac96
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/api/StoreException.java
@@ -0,0 +1,43 @@
+package org.apache.continuum.store.api;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author <a href="mailto:trygvis@inamo.no">Trygve Laugstøl</a>
+ * @version $Id$
+ */
+public class StoreException extends Exception
+{
+
+ public StoreException()
+ {
+ super();
+ }
+
+ public StoreException( String msg )
+ {
+ super( msg );
+ }
+
+ public StoreException( String msg, Exception ex )
+ {
+ super( msg, ex );
+ }
+}
diff --git a/src/main/java/org/apache/continuum/store/jpa/JpaStore.java b/src/main/java/org/apache/continuum/store/jpa/JpaStore.java
new file mode 100644
index 0000000..318e9b4
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/jpa/JpaStore.java
@@ -0,0 +1,93 @@
+/**
+ *
+ */
+package org.apache.continuum.store.jpa;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.continuum.model.CommonUpdatableEntity;
+import org.apache.continuum.store.api.EntityNotFoundException;
+import org.apache.continuum.store.api.Query;
+import org.apache.continuum.store.api.Store;
+import org.apache.continuum.store.api.StoreException;
+import org.springframework.orm.jpa.JpaObjectRetrievalFailureException;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ * @since 1.2
+ */
+public class JpaStore<T extends CommonUpdatableEntity, Q extends Query<T>> extends StoreSupport implements Store<T, Q>
+{
+
+ /**
+ * @{inheritDoc}
+ *
+ * @see org.apache.continuum.store.api.Store#delete(java.lang.Object)
+ */
+ @Transactional( readOnly = false )
+ public void delete( T entity ) throws StoreException
+ {
+ getJpaTemplate().remove( entity );
+ }
+
+ /**
+ * @{inheritDoc}
+ *
+ * @see org.apache.continuum.store.api.Store#lookup(Class, java.lang.Long)
+ */
+ public T lookup( Class<T> klass, Long id ) throws StoreException, EntityNotFoundException
+ {
+ if ( id == null )
+ throw new EntityNotFoundException();
+ T entity = null;
+ try
+ {
+ entity = getJpaTemplate().find( klass, id );
+ }
+ catch ( JpaObjectRetrievalFailureException e )
+ {
+ throw new EntityNotFoundException();
+ }
+ if ( entity == null )
+ throw new EntityNotFoundException();
+ return entity;
+ }
+
+ /**
+ * @{inheritDoc}
+ *
+ * @see org.apache.continuum.store.api.Store#query(org.apache.continuum.store.api.Query)
+ */
+ public List<T> query( Q query ) throws StoreException
+ {
+ Map<String, Object> where = new HashMap<String, Object>();
+ String q = query.toString( where );
+
+ List<T> results = find( q, where, 0, 0 );
+
+ return results;
+ }
+
+ /**
+ * @{inheritDoc}
+ *
+ * @see org.apache.continuum.store.api.Store#save(java.lang.Object)
+ */
+ @Transactional( readOnly = true )
+ public T save( T entity ) throws StoreException
+ {
+ if ( null != entity )
+ {
+ if ( null == entity.getId() )
+ getJpaTemplate().persist( entity );
+ else
+ entity = getJpaTemplate().merge( entity );
+ }
+ return entity;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/store/jpa/JpaStoreFactory.java b/src/main/java/org/apache/continuum/store/jpa/JpaStoreFactory.java
new file mode 100644
index 0000000..4824527
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/jpa/JpaStoreFactory.java
@@ -0,0 +1,70 @@
+/**
+ *
+ */
+package org.apache.continuum.store.jpa;
+
+import org.apache.continuum.model.project.Project;
+import org.apache.continuum.model.project.ProjectGroup;
+import org.apache.continuum.model.project.ProjectNotifier;
+import org.apache.continuum.store.api.Store;
+
+/**
+ * Bean factory that is used by Spring container to create and return instances of {@link Store} implementations.
+ *
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ * @since 1.2
+ */
+public class JpaStoreFactory
+{
+
+ /**
+ * Store instance that services executes requests on underlying store for {@link Project} entities.
+ */
+ private final JpaStore<Project, ProjectQuery<Project>> JPA_PROJECT_STORE =
+ new JpaStore<Project, ProjectQuery<Project>>();
+
+ /**
+ * Store instance that services executes requests on underlying store for {@link ProjectGroup} entities.
+ */
+ private final JpaStore<ProjectGroup, ProjectGroupQuery<ProjectGroup>> JPA_PROJECT_GROUP_STORE =
+ new JpaStore<ProjectGroup, ProjectGroupQuery<ProjectGroup>>();
+
+ /**
+ * Store instance that services executes requests on underlying store for {@link ProjectNotifier} entities.
+ */
+ private final JpaStore<ProjectNotifier, ProjectNotifierQuery<ProjectNotifier>> JPA_PROJECT_NOTIFIER_STORE =
+ new JpaStore<ProjectNotifier, ProjectNotifierQuery<ProjectNotifier>>();
+
+ /**
+ * Returns a {@link Store} instance to service {@link Project} Entity.
+ *
+ * @return a {@link Store} instance to service {@link Project} Entity
+ */
+ public Store<Project, ProjectQuery<Project>> createProjectGroupStoreInstance()
+ {
+ return JPA_PROJECT_STORE;
+ }
+
+ /**
+ * Returns a {@link Store} instance to service {@link ProjectGroup} Entity.
+ *
+ * @return a {@link Store} instance to service {@link ProjectGroup} Entity.
+ */
+ public Store<ProjectGroup, ProjectGroupQuery<ProjectGroup>> createProjectStoreInstance()
+ {
+ return JPA_PROJECT_GROUP_STORE;
+ }
+
+ /**
+ * Returns a {@link Store} instance to service {@link ProjectNotifier} Entity.
+ *
+ * @return a {@link Store} instance to service {@link ProjectNotifier} Entity.
+ */
+ public Store<ProjectNotifier, ProjectNotifierQuery<ProjectNotifier>> createProjectNotifierStoreInstance()
+ {
+
+ return JPA_PROJECT_NOTIFIER_STORE;
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/store/jpa/ProjectGroupQuery.java b/src/main/java/org/apache/continuum/store/jpa/ProjectGroupQuery.java
new file mode 100644
index 0000000..7d4999c
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/jpa/ProjectGroupQuery.java
@@ -0,0 +1,308 @@
+/**
+ *
+ */
+package org.apache.continuum.store.jpa;
+
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.continuum.store.api.Query;
+
+/**
+ * Wraps up retrieval criteria for {@link ProjectGroup}s.
+ *
+ * @author <a href='mailto:rinku@apache.org'>Rahul Thakur</a>
+ * @version $Id$
+ * @since 1.2
+ */
+public class ProjectGroupQuery<ProjectGroup> implements Query<ProjectGroup>
+{
+
+ /**
+ * ProjectGroup creation date criteria.
+ */
+ private Date dateCreated;
+
+ /**
+ * ProjectGroup update date criteria.
+ */
+ private Date dateUpdated;
+
+ /**
+ * ProjectGroup description criteria.
+ */
+ private String description;
+
+ /**
+ * ProjectGroup groupId criteria.
+ */
+ private String groupId;
+
+ /**
+ * ProjectGroup Id criteria.
+ */
+ private Long id;
+
+ /**
+ * ProjectGroup model encoding criteria.
+ */
+ private String modelEncoding;
+
+ /**
+ * ProjectGroup name criteria.
+ */
+ private String name;
+
+ /**
+ * @return
+ *
+ */
+ public Date getDateCreated()
+ {
+ return this.dateCreated;
+ }
+
+ /**
+ * @return
+ * @see org.apache.continuum.model.CommonUpdatableEntity#getDateUpdated()
+ */
+ public Date getDateUpdated()
+ {
+ return this.dateUpdated;
+ }
+
+ /**
+ * @return
+ *
+ */
+ public String getDescription()
+ {
+ return this.description;
+ }
+
+ /**
+ * @return
+ *
+ */
+ public String getGroupId()
+ {
+ return this.groupId;
+ }
+
+ /**
+ * Determine if a date of creation was specified in the query.
+ *
+ * @return <code>true</code> if a date of creation was specified, else <code>false</code>.
+ */
+ public boolean hasDateCreated()
+ {
+ return ( null != this.dateCreated );
+ }
+
+ /**
+ * Determine if an update date was specified in the query.
+ *
+ * @return <code>true</code> if a date of update was specified, else <code>false</code>.
+ */
+ public boolean hasDateUpdated()
+ {
+ return ( null != this.dateUpdated );
+ }
+
+ /**
+ * Determine if there was a Project Group 'description' specified in the query.
+ *
+ * @return <code>true</code> if there was a Project Group 'description' specified , else <code>false</code>.
+ */
+ public boolean hasDescription()
+ {
+ return ( null != this.description && this.description.length() > 0 );
+ }
+
+ /**
+ * Determine if there was a Group Id for the {@link ProjectGroup} specified in the query.
+ *
+ * @return <code>true</code> if there was a Group Id for the {@link ProjectGroup} specified, else
+ * <code>false</code>.
+ */
+ public boolean hasGroupId()
+ {
+ return ( null != this.groupId && this.groupId.length() > 0 );
+ }
+
+ /**
+ *
+ * @return
+ */
+ public boolean hasId()
+ {
+ return ( null != this.id && this.id.longValue() > 0L );
+ }
+
+ /**
+ * Determine if there was a model encoding specified in the query.
+ *
+ * @return <code>true</code> if there was a model encoding specified, else <code>false</code>.
+ */
+ public boolean hasModelEncoding()
+ {
+ return ( null != this.modelEncoding && this.modelEncoding.length() > 0 );
+ }
+
+ /**
+ * Determine if there is a {@link ProjectGroup} name specified in the query.
+ *
+ * @return <code>true</code> if there is a {@link ProjectGroup} name specified, else <code>false</code>.
+ */
+ public boolean hasName()
+ {
+ return ( null != this.name && this.name.length() > 0 );
+ }
+
+ /**
+ * @return
+ */
+ public Long getId()
+ {
+ return this.id;
+ }
+
+ /**
+ * @return
+ */
+ public String getModelEncoding()
+ {
+ return this.modelEncoding;
+ }
+
+ /**
+ * @return
+ */
+ public String getName()
+ {
+ return this.name;
+ }
+
+ /**
+ * @param dateCreated
+ */
+ public void setDateCreated( Date dateCreated )
+ {
+ this.dateCreated = dateCreated;
+ }
+
+ /**
+ * @param dateUpdated
+ */
+ public void setDateUpdated( Date dateUpdated )
+ {
+ this.dateUpdated = dateUpdated;
+ }
+
+ /**
+ * @param description
+ */
+ public void setDescription( String description )
+ {
+ this.description = description;
+ }
+
+ /**
+ * @param groupId
+ */
+ public void setGroupId( String groupId )
+ {
+ this.groupId = groupId;
+ }
+
+ /**
+ * @param id
+ */
+ public void setId( Long id )
+ {
+ this.id = id;
+ }
+
+ /**
+ * @param modelEncoding
+ */
+ public void setModelEncoding( String modelEncoding )
+ {
+ this.modelEncoding = modelEncoding;
+ }
+
+ /**
+ * @param name
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ /**
+ * @{inheritDoc}
+ *
+ * @see org.apache.continuum.store.api.Query#toString(java.util.Map)
+ */
+ public String toString( Map<String, Object> whereClause )
+ {
+ StringBuffer sb = new StringBuffer();
+
+ if ( this.hasId() )
+ {
+ whereClause.put( "id", this.getId() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " projectGroup.id =:id " );
+ }
+ if ( this.hasDateCreated() )
+ {
+ whereClause.put( "dateCreated", this.getDateCreated() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " projectGroup.dateCreated =:dateCreated " );
+ }
+ if ( this.hasDateUpdated() )
+ {
+ whereClause.put( "dateUpdated", this.getDateUpdated() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " projectGroup.dateUpdated =:dateUpdated " );
+ }
+ if ( this.hasDescription() )
+ {
+ whereClause.put( "description", this.getDescription() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " projectGroup.description =:description " );
+ }
+ if ( this.hasGroupId() )
+ {
+ whereClause.put( "groupId", this.getGroupId() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " projectGroup.groupId =:groupId " );
+ }
+ if ( this.hasModelEncoding() )
+ {
+ whereClause.put( "modelEncoding", this.getModelEncoding() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " projectGroup.modelEncoding =:modelEncoding " );
+ }
+ if ( this.hasName() )
+ {
+ whereClause.put( "name", this.getName() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " projectGroup.name =:name " );
+ }
+
+ if ( sb.length() > 0 )
+ sb.insert( 0, " where " );
+ sb.insert( 0, "select projectGroup from ProjectGroup as projectGroup " );
+
+ return sb.toString();
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/store/jpa/ProjectNotifierQuery.java b/src/main/java/org/apache/continuum/store/jpa/ProjectNotifierQuery.java
new file mode 100644
index 0000000..08312b1
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/jpa/ProjectNotifierQuery.java
@@ -0,0 +1,247 @@
+/**
+ *
+ */
+package org.apache.continuum.store.jpa;
+
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.continuum.model.project.Project;
+import org.apache.continuum.model.project.ProjectGroup;
+import org.apache.continuum.store.api.Query;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ * @since 1.2
+ */
+public class ProjectNotifierQuery<ProjectNotifier> implements Query<ProjectNotifier>
+{
+
+ /**
+ * ProjectNotifier creation date criteria.
+ */
+ private Date dateCreated;
+
+ /**
+ * ProjectNotifier update date criteria.
+ */
+ private Date dateUpdated;
+
+ /**
+ * ProjectNotifier Id criteria.
+ */
+ private Long id;
+
+ /**
+ * Determines if a ProjectNotifier is set up on a {@link Project} or a {@link ProjectGroup}.
+ */
+ private boolean isDefinedOnProject = false;
+
+ /**
+ * Determines if a {@link ProjectNotifier} is defined by a user.
+ */
+ private boolean isUserDefined = false;
+
+ /**
+ * ProjectNotifier model encoding criteria.
+ */
+ private String modelEncoding;
+
+ /**
+ * @return
+ *
+ */
+ public Date getDateCreated()
+ {
+ return this.dateCreated;
+ }
+
+ /**
+ * @return
+ * @see org.apache.continuum.model.CommonUpdatableEntity#getDateUpdated()
+ */
+ public Date getDateUpdated()
+ {
+ return this.dateUpdated;
+ }
+
+ /**
+ * @return
+ */
+ public Long getId()
+ {
+ return this.id;
+ }
+
+ /**
+ * @return
+ */
+ public String getModelEncoding()
+ {
+ return this.modelEncoding;
+ }
+
+ /**
+ * Determine if a date of creation was specified in the query.
+ *
+ * @return <code>true</code> if a date of creation was specified, else <code>false</code>.
+ */
+ public boolean hasDateCreated()
+ {
+ return ( null != this.dateCreated );
+ }
+
+ /**
+ * Determine if an update date was specified in the query.
+ *
+ * @return <code>true</code> if a date of update was specified, else <code>false</code>.
+ */
+ public boolean hasDateUpdated()
+ {
+ return ( null != this.dateUpdated );
+ }
+
+ /**
+ *
+ * @return
+ */
+ public boolean hasId()
+ {
+ return ( null != this.id && this.id.longValue() > 0L );
+ }
+
+ /**
+ * Determine if there was a model encoding specified in the query.
+ *
+ * @return <code>true</code> if there was a model encoding specified, else <code>false</code>.
+ */
+ public boolean hasModelEncoding()
+ {
+ return ( null != this.modelEncoding && this.modelEncoding.length() > 0 );
+ }
+
+ /**
+ * @return the isDefinedOnProject
+ */
+ public boolean isDefinedOnProject()
+ {
+ return isDefinedOnProject;
+ }
+
+ /**
+ * @return the isUserDefined
+ */
+ public boolean isUserDefined()
+ {
+ return isUserDefined;
+ }
+
+ /**
+ * @param dateCreated
+ */
+ public void setDateCreated( Date dateCreated )
+ {
+ this.dateCreated = dateCreated;
+ }
+
+ /**
+ * @param dateUpdated
+ */
+ public void setDateUpdated( Date dateUpdated )
+ {
+ this.dateUpdated = dateUpdated;
+ }
+
+ /**
+ * @param isDefinedOnProject
+ * the isDefinedOnProject to set
+ */
+ public void setDefinedOnProject( boolean isDefinedOnProject )
+ {
+ this.isDefinedOnProject = isDefinedOnProject;
+ }
+
+ /**
+ * @param id
+ */
+ public void setId( Long id )
+ {
+ this.id = id;
+ }
+
+ /**
+ * @param modelEncoding
+ */
+ public void setModelEncoding( String modelEncoding )
+ {
+ this.modelEncoding = modelEncoding;
+ }
+
+ /**
+ * @param isUserDefined
+ * the isUserDefined to set
+ */
+ public void setUserDefined( boolean isUserDefined )
+ {
+ this.isUserDefined = isUserDefined;
+ }
+
+ /**
+ * @{inheritDoc}
+ *
+ * @see org.apache.continuum.store.api.Query#toString(java.util.Map)
+ */
+ public String toString( Map<String, Object> whereClause )
+ {
+ StringBuffer sb = new StringBuffer();
+
+ if ( this.hasId() )
+ {
+ whereClause.put( "id", this.getId() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " notifier.id =:id " );
+ }
+ if ( this.hasDateCreated() )
+ {
+ whereClause.put( "dateCreated", this.getDateCreated() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " notifier.dateCreated =:dateCreated " );
+ }
+ if ( this.hasDateUpdated() )
+ {
+ whereClause.put( "dateUpdated", this.getDateUpdated() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " notifier.dateUpdated =:dateUpdated " );
+ }
+ if ( this.hasModelEncoding() )
+ {
+ whereClause.put( "modelEncoding", this.getModelEncoding() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " notifier.modelEncoding =:modelEncoding " );
+ }
+ if ( this.isDefinedOnProject() )
+ {
+ // TODO: Implement!
+ // Need to check what property is setup on the Notifier.
+ // May need to add a property ORM mapping to persist.
+ }
+ if ( this.isUserDefined() )
+ {
+ // TODO: Implement!
+ // Need to check what property is setup on the Notifier.
+ // May need to add a property ORM mapping to persist.
+ }
+
+ if ( sb.length() > 0 )
+ sb.insert( 0, " where " );
+ sb.insert( 0, "select project from Project as project " );
+
+ return sb.toString();
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/store/jpa/ProjectQuery.java b/src/main/java/org/apache/continuum/store/jpa/ProjectQuery.java
new file mode 100644
index 0000000..cc744ba
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/jpa/ProjectQuery.java
@@ -0,0 +1,403 @@
+/**
+ *
+ */
+package org.apache.continuum.store.jpa;
+
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.continuum.store.api.Query;
+
+/**
+ * Wraps up retrieval criteria for {@link Project}s.
+ *
+ * @author <a href='mailto:rinku@apache.org'>Rahul Thakur</a>
+ * @version $Id$
+ * @since 1.2
+ */
+public class ProjectQuery<Project> implements Query<Project>
+{
+
+ /**
+ * Project artifactId criteria.
+ */
+ private String artifactId;
+
+ /**
+ * Project groupId criteria.
+ */
+ private String groupId;
+
+ /**
+ * Project id criteria.
+ */
+ private Long id;
+
+ /**
+ * Project name criteria.
+ */
+ private String name;
+
+ /**
+ * Project Artifact version criteria.
+ */
+ private String version;
+
+ private int buildNumber;
+
+ private Date dateCreated;
+
+ private Date dateUpdated;
+
+ private String description;
+
+ private String modelEncoding;
+
+ /**
+ * @return the artifactId
+ */
+ public String getArtifactId()
+ {
+ return this.artifactId;
+ }
+
+ /**
+ * @return build number as query critera
+ */
+ public int getBuildNumber()
+ {
+ return this.buildNumber;
+ }
+
+ /**
+ * Determines if a build number criteria was specified in the Project query.
+ *
+ * @return
+ */
+ public boolean hasBuildNumber()
+ {
+ return ( this.buildNumber > 0 );
+ }
+
+ /**
+ * @return creation date as query criteria
+ */
+ public Date getDateCreated()
+ {
+ return this.dateCreated;
+ }
+
+ /**
+ * Determines if there was a creation date criteria specified in the ProjectQuery.
+ *
+ * @return <code>true</code> if a creation date was specified, else <code>false</code>.
+ */
+ public boolean hasDateCreated()
+ {
+ return ( null != this.dateCreated );
+ }
+
+ /**
+ * @return last update date from query criteria.
+ */
+ public Date getDateUpdated()
+ {
+ return this.dateUpdated;
+ }
+
+ /**
+ * Determine if there was a last update date criteria specified in the ProjectQuery.
+ *
+ * @return <code>true</code> if there was a last update date criteria specified, else <code>false</code>.
+ */
+ public boolean hasDateUpdated()
+ {
+ return ( null != this.dateUpdated );
+ }
+
+ /**
+ * @return description criteria from the query.
+ */
+ public String getDescription()
+ {
+ return this.description;
+ }
+
+ /**
+ * Determines if there was description criteria specified in the ProjectQuery.
+ *
+ * @return <code>true</code> if there was description criteria specified, else <code>false</code>.
+ */
+ public boolean hasDescription()
+ {
+ return ( null != this.description && this.description.length() > 0 );
+ }
+
+ /**
+ * @return the groupId
+ */
+ public String getGroupId()
+ {
+ return this.groupId;
+ }
+
+ /**
+ * @return the id
+ */
+ public Long getId()
+ {
+ return this.id;
+ }
+
+ /**
+ * @return model encoding criteria from the query.
+ */
+ public String getModelEncoding()
+ {
+ return this.modelEncoding;
+ }
+
+ /**
+ * Determine if there is a model encoding specified in the ProjectQuery.
+ *
+ * @return <code>true</code> if there is a model encoding specified, else <code>false</code>.
+ */
+ public boolean hasModelEncoding()
+ {
+ return ( null != this.modelEncoding && this.modelEncoding.length() > 0 );
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return this.name;
+ }
+
+ /**
+ * @return the version
+ */
+ public String getVersion()
+ {
+ return this.version;
+ }
+
+ /**
+ * Determines if an artifact Id criteria was specified.
+ *
+ * @return
+ */
+ public boolean hasArtifactId()
+ {
+ return ( null != this.artifactId );
+ }
+
+ /**
+ * Determines if a ProjectGroup Id criteria was specified.
+ *
+ * @return
+ */
+ public boolean hasGroupId()
+ {
+ return ( null != this.groupId );
+ }
+
+ /**
+ * Determines if a Project id criteria was specified in the query.
+ *
+ * @return
+ */
+ public boolean hasId()
+ {
+ return ( null != this.id && this.id >= 0 );
+ }
+
+ /**
+ * Determines if a project name criteria was specified.
+ *
+ * @return
+ */
+ public boolean hasName()
+ {
+ return ( null != this.name );
+ }
+
+ /**
+ * Determines if a Version criteria was specified.
+ *
+ * @return
+ */
+ public boolean hasVersion()
+ {
+ return ( null != this.version );
+ }
+
+ /**
+ * @param artifactId
+ * the artifactId to set
+ */
+ public void setArtifactId( String artifactId )
+ {
+ this.artifactId = artifactId;
+ }
+
+ /**
+ * @param buildNumber
+ * criteria to set in the ProjectQuery
+ */
+ public void setBuildNumber( int buildNumber )
+ {
+ this.buildNumber = buildNumber;
+ }
+
+ /**
+ * @param created
+ * criteria to set in the Project Query.
+ */
+ public void setDateCreated( Date dateCreated )
+ {
+ this.dateCreated = dateCreated;
+ }
+
+ /**
+ * @param updated
+ * date criteria to set in the Project Query.
+ */
+ public void setDateUpdated( Date dateUpdated )
+ {
+ this.dateUpdated = dateUpdated;
+ }
+
+ /**
+ * @param description
+ */
+ public void setDescription( String description )
+ {
+ this.description = description;
+ }
+
+ /**
+ * @param groupId
+ * the groupId to set
+ */
+ public void setGroupId( String groupId )
+ {
+ this.groupId = groupId;
+ }
+
+ /**
+ * @param id
+ * the id to set
+ */
+ public void setId( Long id )
+ {
+ this.id = id;
+ }
+
+ /**
+ * @param name
+ * the name to set
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+ /**
+ * @param version
+ * the version to set
+ */
+ public void setVersion( String version )
+ {
+ this.version = version;
+ }
+
+ /**
+ * @{inheritDoc}
+ *
+ * @see org.apache.continuum.store.api.Query#toString(java.util.Map)
+ */
+ public String toString( Map<String, Object> whereClause )
+ {
+ StringBuffer sb = new StringBuffer();
+
+ if ( this.hasId() )
+ {
+ whereClause.put( "id", this.getId() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " project.id =:id " );
+ }
+ if ( this.hasDateCreated() )
+ {
+ whereClause.put( "dateCreated", this.getDateCreated() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " project.dateCreated =:dateCreated " );
+ }
+ if ( this.hasDateUpdated() )
+ {
+ whereClause.put( "dateUpdated", this.getDateUpdated() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " project.dateUpdated =:dateUpdated " );
+ }
+ if ( this.hasDescription() )
+ {
+ whereClause.put( "description", this.getDescription() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " project.description =:description " );
+ }
+ if ( this.hasGroupId() )
+ {
+ whereClause.put( "groupId", this.getGroupId() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " project.groupId =:groupId " );
+ }
+ if ( this.hasModelEncoding() )
+ {
+ whereClause.put( "modelEncoding", this.getModelEncoding() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " project.modelEncoding =:modelEncoding " );
+ }
+ if ( this.hasName() )
+ {
+ whereClause.put( "name", this.getName() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " project.name =:name " );
+ }
+ if ( this.hasArtifactId() )
+ {
+ whereClause.put( "artifactId", this.getArtifactId() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " project.artifactId =:artifactId" );
+ }
+ if ( this.hasBuildNumber() )
+ {
+ whereClause.put( "buildNumber", this.getBuildNumber() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " project.buildNumber =:buildNumber" );
+ }
+ if ( this.hasVersion() )
+ {
+ whereClause.put( "version", this.getVersion() );
+ if ( sb.length() > 0 )
+ sb.append( "and" );
+ sb.append( " project.version =:version" );
+ }
+
+ if ( sb.length() > 0 )
+ sb.insert( 0, " where " );
+ sb.insert( 0, "select project from Project as project " );
+
+ return sb.toString();
+ }
+
+}
diff --git a/src/main/java/org/apache/continuum/store/jpa/StoreSupport.java b/src/main/java/org/apache/continuum/store/jpa/StoreSupport.java
new file mode 100644
index 0000000..15e5e1e
--- /dev/null
+++ b/src/main/java/org/apache/continuum/store/jpa/StoreSupport.java
@@ -0,0 +1,68 @@
+/**
+ *
+ */
+package org.apache.continuum.store.jpa;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+import javax.persistence.Query;
+
+import org.apache.continuum.store.api.Store;
+import org.springframework.dao.DataAccessException;
+import org.springframework.orm.jpa.JpaCallback;
+import org.springframework.orm.jpa.support.JpaDaoSupport;
+
+/**
+ * Base class for concrete {@link Store} implementations that provides service methods for Entity retrievals.
+ *
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ * @since 1.2
+ */
+public abstract class StoreSupport extends JpaDaoSupport
+{
+
+ /**
+ * Prepares and executes a query using the 'where' criteria, a start index and a given range of results to return.
+ *
+ * @param queryString
+ * @param whereParams
+ * @param startIndex
+ * @param range
+ * @return
+ * @throws DataAccessException
+ */
+ protected List find( final String queryString, final Map<String, Object> whereParams, final int startIndex,
+ final int range ) throws DataAccessException
+ {
+ return getJpaTemplate().executeFind( new JpaCallback()
+ {
+ public Object doInJpa( EntityManager em ) throws PersistenceException
+ {
+ Query query = em.createQuery( queryString );
+ if ( whereParams != null )
+ {
+ for ( Iterator it = whereParams.entrySet().iterator(); it.hasNext(); )
+ {
+ Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
+ query.setParameter( entry.getKey(), entry.getValue() );
+ }
+ }
+ if ( startIndex > 0 )
+ {
+ query.setFirstResult( startIndex );
+ }
+ if ( range > 0 )
+ {
+ query.setMaxResults( range );
+ }
+ return query.getResultList();
+ }
+ } );
+ }
+
+}
diff --git a/src/test/java/org/apache/continuum/store/ApplicationContextAwareStoreTestCase.java b/src/test/java/org/apache/continuum/store/ApplicationContextAwareStoreTestCase.java
new file mode 100644
index 0000000..f7bc886
--- /dev/null
+++ b/src/test/java/org/apache/continuum/store/ApplicationContextAwareStoreTestCase.java
@@ -0,0 +1,161 @@
+package org.apache.continuum.store;
+
+import org.apache.continuum.dao.api.GenericDao;
+import org.apache.continuum.model.CommonUpdatableEntity;
+import org.apache.openjpa.persistence.test.SingleEMTestCase;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test Case support class that allows extensions to load test data from specified SQL files.
+ * <p/>
+ * This also implements Spring's {@link ApplicationContextAware} interface that allows the {@link ApplicationContext} to
+ * be made available to this test case's extensions.
+ *
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ * @since 1.2
+ */
+public abstract class ApplicationContextAwareStoreTestCase
+ extends SingleEMTestCase
+ implements ApplicationContextAware
+{
+ /**
+ * Continuum Store persistent unit defined in <code>persistence.xml</code> used by tests.
+ */
+ private static final String PERSISTENT_UNIT_CONTINUUM_STORE = "continuum-store";
+
+ /**
+ * Spring application context.
+ */
+ private ApplicationContext applicationContext;
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * Spring IoC container injects the {@link ApplicationContext} through here.
+ *
+ * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
+ */
+ public void setApplicationContext( ApplicationContext applicationContext )
+ throws BeansException
+ {
+ this.applicationContext = applicationContext;
+ }
+
+ /**
+ * Imports sql from the specified file.
+ *
+ * @param sqlResource Resource containing sql
+ */
+ public void setSqlSource( File sqlResource )
+ {
+ try
+ {
+ // TODO: Use Logger!
+ // System.out.println( "Loading sql: " + sqlResource.getAbsolutePath() );
+ List<String> statements = new ArrayList<String>( 20 );
+ BufferedReader br = new BufferedReader( new InputStreamReader( new FileInputStream( sqlResource ) ) );
+ String line;
+ StringBuffer currentStatement = new StringBuffer();
+ while ( ( line = br.readLine() ) != null )
+ {
+ if ( line.trim().length() == 0 )
+ {
+ continue;
+ }
+ if ( line.trim().startsWith( "#" ) )
+ {
+ continue;
+ }
+
+ currentStatement.append( line );
+ if ( line.endsWith( ";" ) )
+ {
+ statements.add( currentStatement.toString() );
+ currentStatement = new StringBuffer();
+ }
+ }
+ // append a line if missing a ';'
+ if ( currentStatement.length() > 0 )
+ {
+ statements.add( currentStatement.toString() );
+ }
+ runSQLStatements( statements );
+ }
+ catch ( Throwable e )
+ {
+ // TODO: User logger!
+ System.err.println( "Problem executing SQL!" );
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Run a bunch of SQL statements.
+ *
+ * @param statements Statements to run.
+ * @throws SQLException
+ */
+ public void runSQLStatements( final List<String> statements )
+ throws SQLException
+ {
+ for ( String qry : statements )
+ {
+ Connection con = (Connection) this.em.getConnection();
+ try
+ {
+ Statement stmt = con.createStatement();
+ System.out.println( qry );
+ stmt.execute( qry );
+ con.commit();
+ }
+ catch ( SQLException e )
+ {
+ try
+ {
+ con.rollback();
+ }
+ catch ( SQLException e1 )
+ {
+ // TODO: Use logger!
+ System.err.println( "Unable to rollback transaction." );
+ throw e1;
+ }
+ throw e;
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected <T extends CommonUpdatableEntity> GenericDao<T> getDao( String daoBeanReference )
+ {
+ return (GenericDao<T>) getObject( daoBeanReference );
+ }
+
+ protected Object getObject( String beanReference )
+ {
+ return applicationContext.getBean( beanReference );
+ }
+
+ /**
+ * Returns the name of the persistent-unit setup in <code>persistence.xml</code>.
+ */
+ @Override
+ protected String getPersistenceUnitName()
+ {
+ return PERSISTENT_UNIT_CONTINUUM_STORE;
+ }
+
+}
diff --git a/src/test/java/org/apache/continuum/store/jpa/JpaProjectGroupStoreTest.java b/src/test/java/org/apache/continuum/store/jpa/JpaProjectGroupStoreTest.java
new file mode 100644
index 0000000..d2758cb
--- /dev/null
+++ b/src/test/java/org/apache/continuum/store/jpa/JpaProjectGroupStoreTest.java
@@ -0,0 +1,92 @@
+/**
+ *
+ */
+package org.apache.continuum.store.jpa;
+
+import org.apache.continuum.model.project.ProjectGroup;
+import org.apache.continuum.service.api.ProjectGroupService;
+import org.apache.continuum.store.ApplicationContextAwareStoreTestCase;
+import org.apache.continuum.store.api.StoreException;
+import org.apache.openjpa.persistence.OpenJPAQuery;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import java.io.File;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @version $Id$
+ * @see <a
+ * href="http://mail-archives.apache.org/mod_mbox/openjpa-users/200706.mbox/%3CBF2B99E3-7EF3-4E99-91E1-8AEB940524C7@apache.org%3E">
+ * http://mail-archives.apache.org/mod_mbox/openjpa-users/200706.mbox/%3CBF2B99E3-7EF3-4E99-91E1-8AEB940524C7@apache.org%3E
+ * </a>
+ * @since 1.2
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "/META-INF/spring-config.xml")
+public class JpaProjectGroupStoreTest
+ extends ApplicationContextAwareStoreTestCase
+{
+ private static final String BEAN_REF__PROJECT_GROUP_STORE = "projectGroupStore";
+
+ @Override
+ @Before
+ public void setUp()
+ {
+ File testData = new File( "src/test/resources/sql/project-group-table-data.sql" );
+ Assert.assertTrue( "Unable to find test data resource: " + testData.getAbsolutePath(), testData.exists() );
+ Properties propMap = new Properties();
+ setUp( propMap );
+
+ // load test data from SQL file.
+ setSqlSource( testData );
+ }
+
+ @Override
+ @After
+ public void tearDown()
+ throws Exception
+ {
+ super.tearDown();
+ }
+
+ @Test
+ public void testOpenJPASetup()
+ {
+ OpenJPAQuery q = em.createQuery( "select pg from ProjectGroup pg" );
+ String[] sql = q.getDataStoreActions( null );
+ Assert.assertEquals( 1, sql.length );
+ Assert.assertTrue( sql[0].startsWith( "SELECT" ) );
+ List results = q.getResultList();
+ Assert.assertNotNull( results );
+ Assert.assertEquals( 1, results.size() );
+ }
+
+ @Test
+ public void testCreateProjectGroup()
+ throws StoreException
+ {
+ ProjectGroup group = new ProjectGroup();
+ group.setGroupId( "org.sample.group" );
+ group.setName( "Sample Project Group" );
+ group.setDescription( "A sample project group" );
+
+ Assert.assertTrue( null == group.getId() );
+ group = getService().saveOrUpdate( group );
+ Assert.assertTrue( null != group.getId() );
+ Assert.assertTrue( group.getId() > 0L );
+ Assert.assertEquals( 0, group.getProjects().size() );
+ }
+
+ private ProjectGroupService getService()
+ {
+ return (ProjectGroupService) getObject( "projectGroupService" );
+ }
+}
diff --git a/src/test/java/org/apache/continuum/store/jpa/JpaProjectStoreTest.java b/src/test/java/org/apache/continuum/store/jpa/JpaProjectStoreTest.java
new file mode 100644
index 0000000..f6fe8e9
--- /dev/null
+++ b/src/test/java/org/apache/continuum/store/jpa/JpaProjectStoreTest.java
@@ -0,0 +1,120 @@
+package org.apache.continuum.store.jpa;
+
+import org.apache.continuum.dao.api.GenericDao;
+import org.apache.continuum.model.project.Project;
+import org.apache.continuum.service.api.ProjectService;
+import org.apache.continuum.store.ApplicationContextAwareStoreTestCase;
+import org.apache.continuum.store.api.StoreException;
+import org.apache.openjpa.persistence.OpenJPAQuery;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import javax.persistence.Query;
+import java.io.File;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
+ * @author <a href='mailto:evenisse@apache.org'>Emmanuel Venisse</a>
+ * @version $Id$
+ * @see <a
+ * href="http://mail-archives.apache.org/mod_mbox/openjpa-users/200706.mbox/%3CBF2B99E3-7EF3-4E99-91E1-8AEB940524C7@apache.org%3E">
+ * http://mail-archives.apache.org/mod_mbox/openjpa-users/200706.mbox/%3CBF2B99E3-7EF3-4E99-91E1-8AEB940524C7@apache.org%3E
+ * </a>
+ * @since 1.2
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "/META-INF/spring-config.xml")
+public class JpaProjectStoreTest
+ extends ApplicationContextAwareStoreTestCase
+{
+ @Override
+ @Before
+ public void setUp()
+ {
+ File testData = new File( "src/test/resources/sql/project-table-data.sql" );
+ Properties propMap = new Properties();
+ setUp( propMap );
+ // load test data from SQL file.
+ setSqlSource( testData );
+ }
+
+ @Override
+ @After
+ public void tearDown()
+ throws Exception
+ {
+ super.tearDown();
+ }
+
+ @Test
+ public void testOpenJPASetup()
+ {
+ OpenJPAQuery q = em.createQuery( "select p from Project p" );
+ String[] sql = q.getDataStoreActions( null );
+ Assert.assertEquals( 1, sql.length );
+ Assert.assertTrue( sql[0].startsWith( "SELECT" ) );
+ List results = q.getResultList();
+ Assert.assertNotNull( results );
+ Assert.assertEquals( 2, results.size() );
+ }
+
+ @Test
+ public void testNamedQuery()
+ {
+ Query q = em.createNamedQuery( "Project.findAll" );
+ List<Project> projects = q.getResultList();
+ Assert.assertEquals( 2, projects.size() );
+ for ( Project p : projects )
+ {
+ System.out.println( p.getClass().getName() );
+ System.out.println( p );
+ }
+ }
+
+ @Test
+ public void testDao()
+ {
+ GenericDao<Project> dao = getDao( "projectDao" );
+ List<Project> projects = dao.findAll();
+ Assert.assertTrue( 2==projects.size() );
+ }
+
+ @Test
+ public void testCreateProject()
+ throws StoreException
+ {
+ Project project = new Project();
+ project.setArtifactId( "sample-project" );
+ project.setGroupId( "org.sample.group" );
+ project.setName( "Sample Project" );
+ project.setDescription( "A sample project" );
+ project.setScmUseCache( false );
+ project.setScmUrl( "https://localhost/svn/sample-project" );
+
+ Assert.assertTrue( null == project.getId() );
+
+ //store project
+ project = getService().saveOrUpdate( project );
+ Assert.assertTrue( "Identifier of the persisted new Entity should not be null.", null != project.getId() );
+ Assert.assertTrue( "Identifier of the persisted new Entity should be a valid positive value.",
+ project.getId() > 0L );
+ long id = project.getId();
+
+ //search the project
+ Project p = getService().getProject( project.getGroupId(), project.getArtifactId(), project.getVersion() );
+ Assert.assertNotNull( p );
+ Assert.assertTrue( id == p.getId() );
+ }
+
+ private ProjectService getService()
+ {
+ return (ProjectService) getObject( "projectService" );
+ }
+}
diff --git a/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java b/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java
new file mode 100644
index 0000000..6131ed5
--- /dev/null
+++ b/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java
@@ -0,0 +1,218 @@
+/*
+ * 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.openjpa.persistence.test;
+
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+import org.apache.openjpa.kernel.AbstractBrokerFactory;
+import org.apache.openjpa.kernel.Broker;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.persistence.JPAFacadeHelper;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
+import org.junit.Assert;
+
+/**
+ * Base test class providing persistence utilities.
+ */
+public abstract class PersistenceTestCase
+{
+
+ /**
+ * Marker object you an pass to {@link #setUp} to indicate that the database tables should be cleared.
+ */
+ protected static final Object CLEAR_TABLES = new Object();
+
+ /**
+ * Create an entity manager factory. Put {@link #CLEAR_TABLES} in this list to tell the test framework to delete all
+ * table contents before running the tests.
+ *
+ * @param props
+ * list of persistent types used in testing and/or configuration values in the form
+ * key,value,key,value...
+ */
+ protected OpenJPAEntityManagerFactorySPI createEMF( Object... props )
+ {
+ return createNamedEMF( getPersistenceUnitName(), props );
+ }
+
+ /**
+ * The name of the persistence unit that this test class should use by default. This defaults to "test".
+ */
+ protected String getPersistenceUnitName()
+ {
+ return "test";
+ }
+
+ /**
+ * Create an entity manager factory for persistence unit <code>pu</code>. Put {@link #CLEAR_TABLES} in this list
+ * to tell the test framework to delete all table contents before running the tests.
+ *
+ * @param props
+ * list of persistent types used in testing and/or configuration values in the form
+ * key,value,key,value...
+ */
+ protected OpenJPAEntityManagerFactorySPI createNamedEMF( String pu, Object... props )
+ {
+ Map map = new HashMap( System.getProperties() );
+ List<Class> types = new ArrayList<Class>();
+ boolean prop = false;
+ for ( int i = 0; i < props.length; i++ )
+ {
+ if ( prop )
+ {
+ map.put( props[i - 1], props[i] );
+ prop = false;
+ }
+ else if ( props[i] == CLEAR_TABLES )
+ {
+ map.put( "openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true,"
+ + "SchemaAction='add,deleteTableContents')" );
+ }
+ else if ( props[i] instanceof Class )
+ types.add( (Class) props[i] );
+ else if ( props[i] != null )
+ prop = true;
+ }
+
+ if ( !types.isEmpty() )
+ {
+ StringBuffer buf = new StringBuffer();
+ for ( Class c : types )
+ {
+ if ( buf.length() > 0 )
+ buf.append( ";" );
+ buf.append( c.getName() );
+ }
+ map.put( "openjpa.MetaDataFactory", "jpa(Types=" + buf.toString() + ")" );
+ }
+
+ return (OpenJPAEntityManagerFactorySPI) Persistence.createEntityManagerFactory( pu, map );
+ }
+
+ public void tearDown() throws Exception
+ {
+ // super.tearDown();
+ }
+
+ /**
+ * Safely close the given factory.
+ */
+ protected boolean closeEMF( EntityManagerFactory emf )
+ {
+ if ( emf == null )
+ return false;
+ if ( !emf.isOpen() )
+ return false;
+
+ for ( Iterator iter =
+ ( (AbstractBrokerFactory) JPAFacadeHelper.toBrokerFactory( emf ) ).getOpenBrokers().iterator(); iter.hasNext(); )
+ {
+ Broker b = (Broker) iter.next();
+ if ( b != null && !b.isClosed() )
+ {
+ EntityManager em = JPAFacadeHelper.toEntityManager( b );
+ if ( em.getTransaction().isActive() )
+ em.getTransaction().rollback();
+ em.close();
+ }
+ }
+
+ emf.close();
+ return !emf.isOpen();
+ }
+
+ /**
+ * Delete all instances of the given types using bulk delete queries.
+ */
+ protected void clear( EntityManagerFactory emf, Class... types )
+ {
+ if ( emf == null || types.length == 0 )
+ return;
+
+ List<ClassMetaData> metas = new ArrayList<ClassMetaData>( types.length );
+ for ( Class c : types )
+ {
+ ClassMetaData meta = JPAFacadeHelper.getMetaData( emf, c );
+ if ( meta != null )
+ metas.add( meta );
+ }
+ clear( emf, metas.toArray( new ClassMetaData[metas.size()] ) );
+ }
+
+ /**
+ * Delete all instances of the persistent types registered with the given factory using bulk delete queries.
+ */
+ protected void clear( EntityManagerFactory emf )
+ {
+ if ( emf == null )
+ return;
+ clear(
+ emf,
+ ( (OpenJPAEntityManagerFactorySPI) emf ).getConfiguration().getMetaDataRepositoryInstance().getMetaDatas() );
+ }
+
+ /**
+ * Delete all instances of the given types using bulk delete queries.
+ */
+ private void clear( EntityManagerFactory emf, ClassMetaData... types )
+ {
+ if ( emf == null || types.length == 0 )
+ return;
+
+ EntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ for ( ClassMetaData meta : types )
+ {
+ if ( !meta.isMapped() || meta.isEmbeddedOnly()
+ || Modifier.isAbstract( meta.getDescribedType().getModifiers() ) )
+ continue;
+ em.createQuery( "DELETE FROM " + meta.getTypeAlias() + " o" ).executeUpdate();
+ }
+ em.getTransaction().commit();
+ em.close();
+ }
+
+ /**
+ * Return the entity name for the given type.
+ */
+ protected String entityName( EntityManagerFactory emf, Class c )
+ {
+ ClassMetaData meta = JPAFacadeHelper.getMetaData( emf, c );
+ return ( meta == null ) ? null : meta.getTypeAlias();
+ }
+
+ public static void assertNotEquals( Object o1, Object o2 )
+ {
+ if ( o1 == o2 )
+ Assert.fail( "expected args to be different; were the same instance." );
+ else if ( o1 == null || o2 == null )
+ return;
+ else if ( o1.equals( o2 ) )
+ Assert.fail( "expected args to be different; compared equal." );
+ }
+}
diff --git a/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java b/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java
new file mode 100644
index 0000000..2710723
--- /dev/null
+++ b/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java
@@ -0,0 +1,139 @@
+/*
+ * 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.openjpa.persistence.test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.openjpa.lib.jdbc.AbstractJDBCListener;
+import org.apache.openjpa.lib.jdbc.JDBCEvent;
+import org.apache.openjpa.lib.jdbc.JDBCListener;
+import org.junit.Assert;
+
+/**
+ * Base class for tests that need to check generated SQL.
+ *
+ * @author Patrick Linskey
+ */
+public abstract class SQLListenerTestCase extends SingleEMFTestCase
+{
+
+ protected List<String> sql = new ArrayList<String>();
+
+ protected int sqlCount;
+
+ @Override
+ public void setUp( Object... props )
+ {
+ Object[] copy = new Object[props.length + 2];
+ System.arraycopy( props, 0, copy, 0, props.length );
+ copy[copy.length - 2] = "openjpa.jdbc.JDBCListeners";
+ copy[copy.length - 1] = new JDBCListener[] { new Listener() };
+ super.setUp( copy );
+ }
+
+ /**
+ * Confirm that the specified SQL has been executed.
+ *
+ * @param sqlExp
+ * the SQL expression. E.g., "SELECT FOO .*"
+ */
+ public void assertSQL( String sqlExp )
+ {
+ for ( String statement : sql )
+ {
+ if ( statement.matches( sqlExp ) )
+ return;
+ }
+
+ Assert.fail( "Expected regular expression <" + sqlExp + "> to have" + " existed in SQL statements: " + sql );
+ }
+
+ /**
+ * Confirm that the specified SQL has not been executed.
+ *
+ * @param sqlExp
+ * the SQL expression. E.g., "SELECT BADCOLUMN .*"
+ */
+ public void assertNotSQL( String sqlExp )
+ {
+ boolean failed = false;
+
+ for ( String statement : sql )
+ {
+ if ( statement.matches( sqlExp ) )
+ failed = true;
+ }
+
+ if ( failed )
+ Assert.fail( "Regular expression <" + sqlExp + ">" + " should not have been executed in SQL statements: "
+ + sql );
+ }
+
+ /**
+ * Confirm that the executed SQL String contains the specified sqlExp.
+ *
+ * @param sqlExp
+ * the SQL expression. E.g., "SELECT BADCOLUMN .*"
+ */
+ public void assertContainsSQL( String sqlExp )
+ {
+ for ( String statement : sql )
+ {
+ if ( statement.contains( sqlExp ) )
+ return;
+ }
+
+ Assert.fail( "Expected regular expression <" + sqlExp + "> to be" + " contained in SQL statements: " + sql );
+ }
+
+ /**
+ * Gets the number of SQL issued since last reset.
+ */
+ public int getSQLCount()
+ {
+ return sqlCount;
+ }
+
+ /**
+ * Resets SQL count.
+ *
+ * @return number of SQL counted since last reset.
+ */
+ public int resetSQLCount()
+ {
+ int tmp = sqlCount;
+ sqlCount = 0;
+ return tmp;
+ }
+
+ public class Listener extends AbstractJDBCListener
+ {
+
+ @Override
+ public void beforeExecuteStatement( JDBCEvent event )
+ {
+ if ( event.getSQL() != null && sql != null )
+ {
+ sql.add( event.getSQL() );
+ sqlCount++;
+ }
+ }
+ }
+}
diff --git a/src/test/java/org/apache/openjpa/persistence/test/SingleEMFTestCase.java b/src/test/java/org/apache/openjpa/persistence/test/SingleEMFTestCase.java
new file mode 100644
index 0000000..a4f3e75
--- /dev/null
+++ b/src/test/java/org/apache/openjpa/persistence/test/SingleEMFTestCase.java
@@ -0,0 +1,78 @@
+/*
+ * 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.openjpa.persistence.test;
+
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
+
+public abstract class SingleEMFTestCase extends PersistenceTestCase
+{
+
+ protected OpenJPAEntityManagerFactorySPI emf;
+
+ /**
+ * Call {@link #setUp(Object...)} with no arguments so that the emf set-up happens even if <code>setUp()</code> is
+ * not called from the subclass.
+ */
+ public void setUp() throws Exception
+ {
+ setUp( new Object[0] );
+ }
+
+ /**
+ * Initialize entity manager factory. Put {@link #CLEAR_TABLES} in this list to tell the test framework to delete
+ * all table contents before running the tests.
+ *
+ * @param props
+ * list of persistent types used in testing and/or configuration values in the form
+ * key,value,key,value...
+ */
+ protected void setUp( Object... props )
+ {
+ emf = createEMF( props );
+ }
+
+ /**
+ * Closes the entity manager factory.
+ */
+ public void tearDown() throws Exception
+ {
+ super.tearDown();
+
+ if ( emf == null )
+ return;
+
+ try
+ {
+ clear( emf );
+ }
+ finally
+ {
+ closeEMF( emf );
+ }
+ }
+
+ protected ClassMapping getMapping( String name )
+ {
+ return (ClassMapping) emf.getConfiguration().getMetaDataRepositoryInstance().getMetaData(
+ name,
+ getClass().getClassLoader(),
+ true );
+ }
+}
diff --git a/src/test/java/org/apache/openjpa/persistence/test/SingleEMTestCase.java b/src/test/java/org/apache/openjpa/persistence/test/SingleEMTestCase.java
new file mode 100644
index 0000000..3144291
--- /dev/null
+++ b/src/test/java/org/apache/openjpa/persistence/test/SingleEMTestCase.java
@@ -0,0 +1,217 @@
+/*
+ * 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.openjpa.persistence.test;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.persistence.EntityTransaction;
+
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.OpenJPAQuery;
+
+/**
+ * A base test case that can be used to easily test scenarios where there is only a single EntityManager at any given
+ * time.
+ *
+ * @author Marc Prud'hommeaux
+ */
+public abstract class SingleEMTestCase extends SingleEMFTestCase
+{
+
+ protected OpenJPAEntityManager em;
+
+ @Override
+ public void setUp()
+ {
+ setUp( new Object[0] );
+ }
+
+ @Override
+ public void setUp( Object... props )
+ {
+ super.setUp( props );
+ em = emf.createEntityManager();
+ }
+
+ /**
+ * Clear the current EntityManager and re-initialize it.
+ */
+ protected void reset()
+ {
+ close();
+ em = emf.createEntityManager();
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ rollback();
+ close();
+ super.tearDown();
+ }
+
+ /**
+ * Start a new transaction if there isn't currently one active.
+ *
+ * @return true if a transaction was started, false if one already existed
+ */
+ protected boolean begin()
+ {
+ EntityTransaction tx = em.getTransaction();
+ if ( tx.isActive() )
+ return false;
+
+ tx.begin();
+ return true;
+ }
+
+ /**
+ * Commit the current transaction, if it is active.
+ *
+ * @return true if the transaction was committed
+ */
+ protected boolean commit()
+ {
+ EntityTransaction tx = em.getTransaction();
+ if ( !tx.isActive() )
+ return false;
+
+ tx.commit();
+ return true;
+ }
+
+ /**
+ * Rollback the current transaction, if it is active.
+ *
+ * @return true if the transaction was rolled back
+ */
+ protected boolean rollback()
+ {
+ EntityTransaction tx = em.getTransaction();
+ if ( !tx.isActive() )
+ return false;
+
+ tx.rollback();
+ return true;
+ }
+
+ /**
+ * Closes the current EntityManager if it is open.
+ *
+ * @return false if the EntityManager was already closed.
+ */
+ protected boolean close()
+ {
+ if ( em == null )
+ return false;
+
+ rollback();
+
+ if ( !em.isOpen() )
+ return false;
+
+ em.close();
+ return !em.isOpen();
+ }
+
+ /**
+ * Delete all of the instances.
+ *
+ * If no transaction is running, then one will be started and committed. Otherwise, the operation will take place in
+ * the current transaction.
+ */
+ protected void remove( Object... obs )
+ {
+ boolean tx = begin();
+ for ( Object ob : obs )
+ em.remove( ob );
+ if ( tx )
+ commit();
+ }
+
+ /**
+ * Persist all of the instances.
+ *
+ * If no transaction is running, then one will be started and committed. Otherwise, the operation will take place in
+ * the current transaction.
+ */
+ protected void persist( Object... obs )
+ {
+ boolean tx = begin();
+ for ( Object ob : obs )
+ em.persist( ob );
+ if ( tx )
+ commit();
+ }
+
+ /**
+ * Creates a query in the current EntityManager with the specified string.
+ */
+ protected OpenJPAQuery query( String str )
+ {
+ return em.createQuery( str );
+ }
+
+ /**
+ * Create a query against the specified class, which will be aliased as "x". For example, query(Person.class, "where
+ * x.age = 21") will create the query "select x from Person x where x.age = 21".
+ *
+ * @param c
+ * the class to query against
+ * @param str
+ * the query suffix
+ * @param params
+ * the parameters, if any
+ * @return the Query object
+ */
+ protected OpenJPAQuery query( Class c, String str, Object... params )
+ {
+ String query = "select x from " + entityName( emf, c ) + " x " + ( str == null ? "" : str );
+ OpenJPAQuery q = em.createQuery( query );
+ for ( int i = 0; params != null && i < params.length; i++ )
+ q.setParameter( i + 1, params[i] );
+ return q;
+ }
+
+ /**
+ * Returns a list of all instances of the specific class in the database.
+ *
+ * @param c
+ * the class to find
+ * @param q
+ * the query string suffix to use
+ * @param params
+ * the positional parameter list value
+ *
+ * @see #query(java.lang.Class,java.lang.String)
+ */
+ protected <E> List<E> find( Class<E> c, String q, Object... params )
+ {
+ return Collections.checkedList( query( c, q, params ).getResultList(), c );
+ }
+
+ /**
+ * Returns a list of all instances of the specific class in the database.
+ */
+ protected <E> List<E> find( Class<E> c )
+ {
+ return find( c, null );
+ }
+}
diff --git a/src/test/resources/META-INF/org.apache.openjpa.lib.conf.ProductDerivation b/src/test/resources/META-INF/org.apache.openjpa.lib.conf.ProductDerivation
new file mode 100644
index 0000000..f958c6d
--- /dev/null
+++ b/src/test/resources/META-INF/org.apache.openjpa.lib.conf.ProductDerivation
@@ -0,0 +1,17 @@
+# 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.
+org.apache.openjpa.persistence.jdbc.JDBCPersistenceProductDerivation
diff --git a/src/test/resources/META-INF/persistence.xml b/src/test/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..803f3e4
--- /dev/null
+++ b/src/test/resources/META-INF/persistence.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
+ <persistence-unit name="continuum-store">
+ <!-- provider>org.hibernate.ejb.HibernatePersistence</provider -->
+ <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
+
+ <!-- Mapped superclasses -->
+ <class>org.apache.continuum.model.CommonPersistableEntity</class>
+ <class>org.apache.continuum.model.CommonCreatedEntity</class>
+ <class>org.apache.continuum.model.CommonUpdatableEntity</class>
+
+ <!-- Entities -->
+ <class>org.apache.continuum.model.project.BuildDefinition</class>
+ <class>org.apache.continuum.model.project.BuildDefinitionTemplate</class>
+ <class>org.apache.continuum.model.project.BuildResult</class>
+ <class>org.apache.continuum.model.project.Project</class>
+ <class>org.apache.continuum.model.project.ProjectDependency</class>
+ <class>org.apache.continuum.model.project.ProjectDeveloper</class>
+ <class>org.apache.continuum.model.project.ProjectGroup</class>
+ <class>org.apache.continuum.model.project.ProjectNotifier</class>
+ <class>org.apache.continuum.model.project.Schedule</class>
+
+ <class>org.apache.continuum.model.scm.ChangeFile</class>
+ <class>org.apache.continuum.model.scm.ChangeSet</class>
+ <class>org.apache.continuum.model.scm.ScmResult</class>
+ <class>org.apache.continuum.model.scm.SuiteResult</class>
+ <class>org.apache.continuum.model.scm.TestCaseFailure</class>
+ <class>org.apache.continuum.model.scm.TestResult</class>
+
+ <class>org.apache.continuum.model.system.Installation</class>
+ <class>org.apache.continuum.model.system.NotificationAddress</class>
+ <class>org.apache.continuum.model.system.Profile</class>
+ <class>org.apache.continuum.model.system.SystemConfiguration</class>
+
+ <properties>
+ <property name="openjpa.jdbc.DBDictionary" value="hsql" />
+ <property name="openjpa.ConnectionURL" value="jdbc:hsqldb:mem:continuum_jpa;create=true,autoCommit=true"/>
+ <property name="openjpa.ConnectionDriverName" value="org.hsqldb.jdbcDriver"/>
+ <property name="openjpa.ConnectionUserName" value="sa"/>
+ <property name="openjpa.ConnectionPassword" value=""/>
+ <!-- Configure OpenJPA to automatically run the mapping tool at runtime and create schema on Unit Test setup -->
+ <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
+
+ <!-- Enable SQL logging in OpenJPA
+ <property name="openjpa.Log" value="DefaultLevel=INFO,SQL=TRACE" />
+ -->
+
+ <!-- Change default log level across OpenJPA -->
+ <property name="openjpa.Log" value="DefaultLevel=WARN"/>
+
+ </properties>
+
+ </persistence-unit>
+</persistence>
\ No newline at end of file
diff --git a/src/test/resources/META-INF/spring-config.xml b/src/test/resources/META-INF/spring-config.xml
new file mode 100644
index 0000000..8e52903
--- /dev/null
+++ b/src/test/resources/META-INF/spring-config.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:tx="http://www.springframework.org/schema/tx"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
+ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
+
+
+ <bean id="entityManagerFactory"
+ class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
+ <property name="persistenceUnitName" value="continuum-store" />
+ </bean>
+
+ <!-- Setup the transaction manager -->
+ <bean id="transactionManager"
+ class="org.springframework.orm.jpa.JpaTransactionManager">
+ <property name="entityManagerFactory" ref="entityManagerFactory" />
+ </bean>
+ <bean id="transactionInterceptor"
+ class="org.springframework.transaction.interceptor.TransactionInterceptor">
+ <property name="transactionManager" ref="transactionManager" />
+ <property name="transactionAttributeSource">
+ <bean
+ class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource" />
+ </property>
+ </bean>
+
+ <!-- enable the configuration of transactional behavior based on annotations -->
+ <tx:annotation-driven transaction-manager="transactionManager" />
+
+ <bean id="storeFactory"
+ class="org.apache.continuum.store.jpa.JpaStoreFactory">
+ </bean>
+
+ <bean id="projectStore"
+ class="org.apache.continuum.store.jpa.JpaStore"
+ factory-bean="storeFactory"
+ factory-method="createProjectStoreInstance">
+ <property name="entityManagerFactory" ref="entityManagerFactory" />
+ </bean>
+
+ <bean id="projectGroupStore"
+ class="org.apache.continuum.store.jpa.JpaStore"
+ factory-bean="storeFactory"
+ factory-method="createProjectGroupStoreInstance">
+ <property name="entityManagerFactory" ref="entityManagerFactory" />
+ </bean>
+
+ <bean id="projectNotifierStore"
+ class="org.apache.continuum.store.jpa.JpaStore"
+ factory-bean="storeFactory"
+ factory-method="createProjectNotifierStoreInstance">
+ <property name="entityManagerFactory" ref="entityManagerFactory" />
+ </bean>
+
+
+ <!-- DAO -->
+
+ <bean id="daoFactory"
+ class="org.apache.continuum.dao.impl.DaoFactoryImpl">
+ </bean>
+
+ <bean id="projectGroupDao"
+ class="org.apache.continuum.dao.impl.GenericDaoJpa"
+ factory-bean="daoFactory"
+ factory-method="createProjectGroupDao">
+ <property name="entityManagerFactory" ref="entityManagerFactory" />
+ </bean>
+
+ <bean id="projectDao"
+ class="org.apache.continuum.dao.impl.GenericDaoJpa"
+ factory-bean="daoFactory"
+ factory-method="createProjectDao">
+ <property name="entityManagerFactory" ref="entityManagerFactory" />
+ </bean>
+
+ <bean id="notifierDao"
+ class="org.apache.continuum.dao.impl.GenericDaoJpa"
+ factory-bean="daoFactory"
+ factory-method="createNotifierDao">
+ <property name="entityManagerFactory" ref="entityManagerFactory" />
+ </bean>
+
+ <!-- SERVICES -->
+
+ <bean id="projectGroupService"
+ class="org.apache.continuum.service.impl.ProjectGroupServiceImpl">
+ <property name="projectGroupDao" ref="projectGroupDao"/>
+ <property name="projectDao" ref="projectDao"/>
+ </bean>
+
+ <bean id="projectService"
+ class="org.apache.continuum.service.impl.ProjectServiceImpl">
+ <property name="projectDao" ref="projectGroupDao"/>
+ <property name="notifierDao" ref="notifierDao"/>
+ </bean>
+
+</beans>
\ No newline at end of file
diff --git a/src/test/resources/sql/project-group-table-data.sql b/src/test/resources/sql/project-group-table-data.sql
new file mode 100644
index 0000000..db4f53d
--- /dev/null
+++ b/src/test/resources/sql/project-group-table-data.sql
@@ -0,0 +1,5 @@
+# Test SQL to populate data here
+
+
+INSERT INTO PROJECT_GROUP ( ID, DATE_CREATED, DESCRIPTION, GROUP_ID, NAME)
+ VALUES ( 100, 2007-11-10, 'Group for Continuum sub-projects', 'org.apache.continuum', 'Continuum Projects')
diff --git a/src/test/resources/sql/project-notifier-table-data.sql b/src/test/resources/sql/project-notifier-table-data.sql
new file mode 100644
index 0000000..3d92286
--- /dev/null
+++ b/src/test/resources/sql/project-notifier-table-data.sql
@@ -0,0 +1,5 @@
+# Test data for Project Notifier tests
+
+INSERT INTO PROJECT_NOTIFIER
+ (ID, DATE_CREATED, DATE_UPDATED, FLG_ENABLED, NOTIFIER_ORIGIN, RECIPIENT_TYPE, FLG_SEND_ON_ERROR,FLG_SEND_ON_FAILURE, FLG_SEND_ON_SUCCESS, FLG_SEND_ON_WARNING)
+ VALUES (100, 2007-11-01, 2007-11-10, 1, 1, 1 , 1, 0, 0, 0 )
\ No newline at end of file
diff --git a/src/test/resources/sql/project-table-data.sql b/src/test/resources/sql/project-table-data.sql
new file mode 100644
index 0000000..ec7a5ea
--- /dev/null
+++ b/src/test/resources/sql/project-table-data.sql
@@ -0,0 +1,7 @@
+# Test SQL to populate data here
+
+INSERT INTO PROJECT (ID, DATE_CREATED, DATE_UPDATED, ARTIFACT_ID, GROUP_ID, DESCRIPTION, NAME, FLG_SCM_USE_CACHE)
+ VALUES (100, 2007-11-01, 2007-11-10, 'continuum-jpa-model', 'org.apache.continuum', 'Model for Continuum using JPA', 'continuum-jpa-model', false);
+
+INSERT INTO PROJECT (ID, DATE_CREATED, DATE_UPDATED, ARTIFACT_ID, GROUP_ID, DESCRIPTION, NAME, FLG_SCM_USE_CACHE)
+ VALUES (101, 2007-11-02, 2007-11-11, 'continuum-jdo-model', 'org.apache.continuum', 'Legacy Model for Continuum using JDO', 'continuum-jdo-model', true);
\ No newline at end of file