Added subclass mapping to JobInstance to support existing JobIdentifier strategies.

OPEN - issue BATCH-84: Simplify JobLauncher so it can be used to start a job with a String/Properties 
http://opensource.atlassian.com/projects/spring/browse/BATCH-84
This commit is contained in:
dsyer
2007-08-28 10:33:20 +00:00
parent a7fd5eac3b
commit fd3d6d6650
6 changed files with 249 additions and 47 deletions

View File

@@ -0,0 +1,90 @@
package org.springframework.batch.execution.repository.dao;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.hibernate.EmptyInterceptor;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.runtime.JobIdentifier;
import org.springframework.batch.execution.runtime.ScheduledJobIdentifier;
import org.springframework.util.ClassUtils;
/**
* Hibernate interceptor that can distinguish between the various
* {@link JobIdentifier} strategies in a {@link JobInstance}. Its task is to
* return the correct entity name based on the type of {@link JobIdentifier}
* used. There is necessarily some tight coupling between this and the Hibernate
* mappings for {@link JobInstance} because the map from {@link JobIdentifier}
* type to entity name is in both places.
*
*
* @author Dave Syer
*
*/
public class EntityNameInterceptor extends EmptyInterceptor {
private static final String SIMPLE_JOB_INSTANCE = "SimpleJobInstance";
private Map identifierTypes = new HashMap();
{
identifierTypes.put(ScheduledJobIdentifier.class,
"ScheduledJobInstance");
};
/**
* Public setter for the identifier types. A map from Class (the
* {@link JobIdentifier} implementation) to String (the entity name). If a
* map from String to String is provided it will be interpreted as a map
* from class name to entity name.
*
* @throws IllegalArgumentException
* if a String key is provided that is not a Class name.
*
* @param types
* the identifierTypes to set
*/
public void setIdentifierTypes(Map types) {
this.identifierTypes = new HashMap();
for (Iterator iterator = types.entrySet().iterator(); iterator
.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
Object key = entry.getKey();
if (key instanceof Class) {
identifierTypes.put(key, entry.getValue());
} else {
try {
Class classKey = ClassUtils.forName(key.toString());
identifierTypes.put(classKey, entry.getValue());
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(
"Could not convert key in identifierTypes to type Class: ["
+ key + "]", e);
}
}
}
}
/**
* If the object is a {@link JobInstance} search the identifier types for an
* entity name based on the {@link JobIdentifier} type. Fall back to
* SimpleJobIdentifier if the value is not found.
*
* @see org.hibernate.EmptyInterceptor#getEntityName(java.lang.Object)
*/
public String getEntityName(Object object) {
if (object instanceof JobInstance) {
JobInstance instance = (JobInstance) object;
for (Iterator iterator = identifierTypes.entrySet().iterator(); iterator
.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
Class key = (Class) entry.getKey();
if (key.isAssignableFrom(instance.getIdentifier().getClass())) {
return (String) entry.getValue();
}
}
return SIMPLE_JOB_INSTANCE;
}
return super.getEntityName(object);
}
}

View File

@@ -49,9 +49,7 @@ public class HibernateJobDao extends HibernateDaoSupport implements JobDao {
*/
public JobInstance createJob(JobIdentifier jobIdentifier) {
ScheduledJobIdentifier jobRuntimeInformation = (ScheduledJobIdentifier) jobIdentifier;
validateJobIdentifier(jobRuntimeInformation);
validateJobIdentifier(jobIdentifier);
JobInstance job = new JobInstance();
job.setIdentifier(jobIdentifier);
@@ -75,13 +73,16 @@ public class HibernateJobDao extends HibernateDaoSupport implements JobDao {
validateJobIdentifier(jobRuntimeInformation);
List list = this.getHibernateTemplate().executeFind(new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session.createCriteria(JobInstance.class);
criteria.add(Expression.eq("identifier", jobRuntimeInformation));
return criteria.list();
}
});
List list = this.getHibernateTemplate().executeFind(
new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session
.createCriteria(JobInstance.class);
criteria.add(Expression.eq("identifier",
jobRuntimeInformation));
return criteria.list();
}
});
return list;
}
@@ -93,12 +94,16 @@ public class HibernateJobDao extends HibernateDaoSupport implements JobDao {
Assert.notNull(jobId, "JobId cannot be null");
Long result = (Long) this.getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) {
return session.createQuery("select count(id) from JobExecution where jobId = :jobId").setLong("jobId",
jobId.longValue()).uniqueResult();
}
});
Long result = (Long) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session) {
return session
.createQuery(
"select count(id) from JobExecution where jobId = :jobId")
.setLong("jobId", jobId.longValue())
.uniqueResult();
}
});
return (result == null) ? 0 : result.intValue();
}
@@ -137,13 +142,16 @@ public class HibernateJobDao extends HibernateDaoSupport implements JobDao {
validateJobExecution(jobExecution);
if (jobExecution.getId() == null) {
throw new IllegalArgumentException("JobExecution ID cannot be null. JobExecution must be saved "
+ "before it can be updated.");
throw new IllegalArgumentException(
"JobExecution ID cannot be null. JobExecution must be saved "
+ "before it can be updated.");
}
if (getHibernateTemplate().get(JobExecution.class, jobExecution.getId()) == null) {
throw new NoSuchBatchDomainObjectException("Invalid JobExecution, ID " + jobExecution.getId()
+ " not found.");
if (getHibernateTemplate()
.get(JobExecution.class, jobExecution.getId()) == null) {
throw new NoSuchBatchDomainObjectException(
"Invalid JobExecution, ID " + jobExecution.getId()
+ " not found.");
}
getHibernateTemplate().update(jobExecution);
@@ -156,13 +164,15 @@ public class HibernateJobDao extends HibernateDaoSupport implements JobDao {
final Long jobId = job.getId();
List list = this.getHibernateTemplate().executeFind(new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session.createCriteria(JobExecution.class);
criteria.add(Expression.eq("jobId", jobId));
return criteria.list();
}
});
List list = this.getHibernateTemplate().executeFind(
new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session
.createCriteria(JobExecution.class);
criteria.add(Expression.eq("jobId", jobId));
return criteria.list();
}
});
return list;
}
@@ -176,9 +186,12 @@ public class HibernateJobDao extends HibernateDaoSupport implements JobDao {
private void validateJobExecution(JobExecution jobExecution) {
Assert.notNull(jobExecution);
Assert.notNull(jobExecution.getJobId(), "JobExecution Job-Id cannot be null.");
Assert.notNull(jobExecution.getStartTime(), "JobExecution start time cannot be null.");
Assert.notNull(jobExecution.getStatus(), "JobExecution status cannot be null.");
Assert.notNull(jobExecution.getJobId(),
"JobExecution Job-Id cannot be null.");
Assert.notNull(jobExecution.getStartTime(),
"JobExecution start time cannot be null.");
Assert.notNull(jobExecution.getStatus(),
"JobExecution status cannot be null.");
}
/*
@@ -186,11 +199,19 @@ public class HibernateJobDao extends HibernateDaoSupport implements JobDao {
* acceptable for any field to be blank, however null fields may cause odd
* and vague exception reports from the database driver.
*/
private void validateJobIdentifier(ScheduledJobIdentifier jobRuntimeInformation) {
private void validateJobIdentifier(JobIdentifier jobIdentifier) {
Assert.notNull(jobRuntimeInformation, "JobRuntimeInformation cannot be null.");
Assert.notNull(jobRuntimeInformation.getName(), "JobRuntimeInformation name cannot be null.");
Assert.notNull(jobRuntimeInformation.getJobStream(), "JobRuntimeInformation JobStream cannot be null.");
Assert.notNull(jobRuntimeInformation.getScheduleDate(), "JobRuntimeInformation ScheduleDate cannot be null.");
Assert.notNull(jobIdentifier, "JobRuntimeInformation cannot be null.");
Assert.notNull(jobIdentifier.getName(),
"JobRuntimeInformation name cannot be null.");
if (jobIdentifier instanceof ScheduledJobIdentifier) {
ScheduledJobIdentifier jobRuntimeInformation = (ScheduledJobIdentifier) jobIdentifier;
Assert.notNull(jobRuntimeInformation.getJobStream(),
"JobRuntimeInformation JobStream cannot be null.");
Assert.notNull(jobRuntimeInformation.getScheduleDate(),
"JobRuntimeInformation ScheduleDate cannot be null.");
}
}
}

View File

@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
@@ -8,18 +8,42 @@
<hibernate-mapping package="org.springframework.batch.core.domain">
<class name="JobInstance" table="BATCH_JOB">
<id name="id" type="long" column="ID">
&job-generator;
</id>
<version name="version" access="field" />
<component name="identifier" class="org.springframework.batch.execution.runtime.ScheduledJobIdentifier">
<property name="name" update="false" column="JOB_NAME" />
<property name="jobStream" update="false" column="JOB_STREAM" />
<property name="scheduleDate" update="false" column="SCHEDULE_DATE" />
<property name="jobRun" update="false" column="JOB_RUN" />
</component>
<property name="status" type="org.springframework.batch.execution.repository.dao.BatchStatusUserType"
insert="false" column="STATUS" />
</class>
<discriminator
formula="case when SCHEDULE_DATE is NULL then 'Scheduled' else 'Simple' end"
type="string" />
<version name="version" access="field" />
<property name="status"
type="org.springframework.batch.execution.repository.dao.BatchStatusUserType"
insert="false" column="STATUS" />
<subclass entity-name="ScheduledJobInstance" name="JobInstance"
discriminator-value="Scheduled">
<component name="identifier"
class="org.springframework.batch.execution.runtime.ScheduledJobIdentifier">
<property name="name" update="false" column="JOB_NAME" />
<property name="jobStream" update="false"
column="JOB_STREAM" />
<property name="scheduleDate" update="false"
column="SCHEDULE_DATE" />
<property name="jobRun" update="false" column="JOB_RUN" />
</component>
</subclass>
<subclass entity-name="SimpleJobInstance" name="JobInstance"
discriminator-value="Simple">
<component name="identifier"
class="org.springframework.batch.core.runtime.SimpleJobIdentifier">
<property name="name" update="false" column="JOB_NAME" />
</component>
</subclass>
</class>
</hibernate-mapping>

View File

@@ -0,0 +1,49 @@
package org.springframework.batch.execution.repository.dao;
import java.util.Collections;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.runtime.SimpleJobIdentifier;
import org.springframework.batch.execution.runtime.ScheduledJobIdentifier;
import junit.framework.TestCase;
public class EntityNameInterceptorTests extends TestCase {
private EntityNameInterceptor interceptor = new EntityNameInterceptor();
public void testGetEntityNameForScheduledJobIdentifier() {
JobInstance job = new JobInstance();
job.setIdentifier(new ScheduledJobIdentifier("foo"));
assertEquals("ScheduledJobInstance", interceptor.getEntityName(job));
}
public void testGetEntityNameForSimpleJobIdentifier() {
JobInstance job = new JobInstance();
job.setIdentifier(new SimpleJobIdentifier("foo"));
assertEquals("SimpleJobInstance", interceptor.getEntityName(job));
}
public void testSetIdentifierTypesWithString() {
interceptor.setIdentifierTypes(Collections.singletonMap(ScheduledJobIdentifier.class.getName(), "foo"));
JobInstance job = new JobInstance();
job.setIdentifier(new ScheduledJobIdentifier("foo"));
assertEquals("foo", interceptor.getEntityName(job));
}
public void testSetIdentifierTypesWithClass() {
interceptor.setIdentifierTypes(Collections.singletonMap(ScheduledJobIdentifier.class, "foo"));
JobInstance job = new JobInstance();
job.setIdentifier(new ScheduledJobIdentifier("foo"));
assertEquals("foo", interceptor.getEntityName(job));
}
public void testSetIdentifierTypesWithInvalidClassName() {
try {
interceptor.setIdentifierTypes(Collections.singletonMap("FooBarNotAClass", "foo"));
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected
}
}
}

View File

@@ -22,6 +22,7 @@ import java.util.Map;
import org.hibernate.SessionFactory;
import org.springframework.batch.core.domain.BatchStatus;
import org.springframework.batch.core.runtime.SimpleJobIdentifier;
import org.springframework.util.ClassUtils;
public class HibernateJobDaoTests extends SqlJobDaoTests {
@@ -49,4 +50,18 @@ public class HibernateJobDaoTests extends SqlJobDaoTests {
assertEquals(jobExecution.getEndTime(), ((Map)executions.get(0)).get("END_TIME"));
}
public void testJobWithSimpleJobIdentifier() throws Exception {
SimpleJobIdentifier jobIdentifier = new SimpleJobIdentifier("Job1");
// Create job.
job = jobDao.createJob(jobIdentifier);
sessionFactory.getCurrentSession().flush();
List jobs = jdbcTemplate.queryForList("SELECT * FROM BATCH_JOB where ID=?", new Object[] {job.getId()});
assertEquals(1, jobs.size());
assertEquals(job.getName(), ((Map)jobs.get(0)).get("JOB_NAME"));
}
}

View File

@@ -24,6 +24,9 @@
<property name="dataSource" ref="dataSource" />
<property name="lobHandler">
<bean class="org.springframework.jdbc.support.lob.DefaultLobHandler"/>
</property>
<property name="entityInterceptor">
<bean class="org.springframework.batch.execution.repository.dao.EntityNameInterceptor"/>
</property>
</bean>