diff --git a/execution/src/main/java/org/springframework/batch/execution/repository/dao/EntityNameInterceptor.java b/execution/src/main/java/org/springframework/batch/execution/repository/dao/EntityNameInterceptor.java new file mode 100644 index 000000000..5d9d1aadb --- /dev/null +++ b/execution/src/main/java/org/springframework/batch/execution/repository/dao/EntityNameInterceptor.java @@ -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); + } +} diff --git a/execution/src/main/java/org/springframework/batch/execution/repository/dao/HibernateJobDao.java b/execution/src/main/java/org/springframework/batch/execution/repository/dao/HibernateJobDao.java index f77c12577..2192f9172 100644 --- a/execution/src/main/java/org/springframework/batch/execution/repository/dao/HibernateJobDao.java +++ b/execution/src/main/java/org/springframework/batch/execution/repository/dao/HibernateJobDao.java @@ -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."); + } } } diff --git a/execution/src/main/resources/org/springframework/batch/execution/repository/dao/JobInstance.hbm.xml b/execution/src/main/resources/org/springframework/batch/execution/repository/dao/JobInstance.hbm.xml index 81c3bdd12..874944459 100644 --- a/execution/src/main/resources/org/springframework/batch/execution/repository/dao/JobInstance.hbm.xml +++ b/execution/src/main/resources/org/springframework/batch/execution/repository/dao/JobInstance.hbm.xml @@ -1,4 +1,4 @@ - + + &job-generator; - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/execution/src/test/java/org/springframework/batch/execution/repository/dao/EntityNameInterceptorTests.java b/execution/src/test/java/org/springframework/batch/execution/repository/dao/EntityNameInterceptorTests.java new file mode 100644 index 000000000..97e1f7261 --- /dev/null +++ b/execution/src/test/java/org/springframework/batch/execution/repository/dao/EntityNameInterceptorTests.java @@ -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 + } + } +} diff --git a/execution/src/test/java/org/springframework/batch/execution/repository/dao/HibernateJobDaoTests.java b/execution/src/test/java/org/springframework/batch/execution/repository/dao/HibernateJobDaoTests.java index a8e29ae63..b43ec6b63 100644 --- a/execution/src/test/java/org/springframework/batch/execution/repository/dao/HibernateJobDaoTests.java +++ b/execution/src/test/java/org/springframework/batch/execution/repository/dao/HibernateJobDaoTests.java @@ -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")); + + } + } diff --git a/execution/src/test/resources/org/springframework/batch/execution/repository/dao/hibernate-context.xml b/execution/src/test/resources/org/springframework/batch/execution/repository/dao/hibernate-context.xml index cabd910c8..cda532b01 100644 --- a/execution/src/test/resources/org/springframework/batch/execution/repository/dao/hibernate-context.xml +++ b/execution/src/test/resources/org/springframework/batch/execution/repository/dao/hibernate-context.xml @@ -24,6 +24,9 @@ + + +