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 index e597c3a1b..a9947c759 100644 --- 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 @@ -1,18 +1,10 @@ package org.springframework.batch.execution.repository.dao; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - import org.hibernate.EmptyInterceptor; import org.springframework.batch.core.domain.JobIdentifier; import org.springframework.batch.core.domain.JobInstance; -import org.springframework.batch.execution.runtime.DefaultJobIdentifier; -import org.springframework.batch.execution.runtime.ScheduledJobIdentifier; -import org.springframework.util.ClassUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; /** * Hibernate interceptor that can distinguish between the various @@ -26,56 +18,25 @@ import org.springframework.util.ClassUtils; * @author Dave Syer * */ -public class EntityNameInterceptor extends EmptyInterceptor { +public class EntityNameInterceptor extends EmptyInterceptor implements InitializingBean { - private static final String SIMPLE_JOB_INSTANCE = "SimpleJobInstance"; - private Map identifierTypes; - private Set entrySet; - - public EntityNameInterceptor() { - Map types = new HashMap(); - types.put(ScheduledJobIdentifier.class, - "ScheduledJobInstance"); - types.put(DefaultJobIdentifier.class, "DefaultJobInstance"); - setIdentifierTypes(types); - }; + private EntityNameLocator entityNameLocator; /** - * 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 setter for the {@link EntityNameLocator} property. + * + * @param entityNameLocator the entityNameLocator 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); - } - } - } - - this.entrySet = new TreeSet(new ClassComparator()); - entrySet.addAll(identifierTypes.keySet()); - + public void setEntityNameLocator(EntityNameLocator entityNameLocator) { + this.entityNameLocator = entityNameLocator; + } + + /** + * Check mandatory properties ({@link #entityNameLocator}). + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws Exception { + Assert.notNull(entityNameLocator, "EntityNameLocator must be provided."); } /** @@ -88,38 +49,10 @@ public class EntityNameInterceptor extends EmptyInterceptor { public String getEntityName(Object object) { if (object instanceof JobInstance) { JobInstance instance = (JobInstance) object; - - for (Iterator iterator = entrySet.iterator(); iterator.hasNext();) { - Class key = (Class) iterator.next(); - if (key.isAssignableFrom(instance.getIdentifier().getClass())) { - return (String) identifierTypes.get(key); - } - } - return SIMPLE_JOB_INSTANCE; + return entityNameLocator + .locate(instance.getIdentifier().getClass()); } return super.getEntityName(object); } - /** - * Comparator for classes to order by inheritance. - * - * @author Dave Syer - * - */ - private class ClassComparator implements Comparator { - /** - * @return 1 if arg0 is assignable from arg1 - * @return -1 otherwise - * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) - */ - public int compare(Object arg0, Object arg1) { - Class cls0 = (Class) arg0; - Class cls1 = (Class) arg1; - if (cls0.isAssignableFrom(cls1)) { - return 1; - } - return -1; - } - } - } diff --git a/execution/src/main/java/org/springframework/batch/execution/repository/dao/EntityNameLocator.java b/execution/src/main/java/org/springframework/batch/execution/repository/dao/EntityNameLocator.java new file mode 100644 index 000000000..ad270b3d1 --- /dev/null +++ b/execution/src/main/java/org/springframework/batch/execution/repository/dao/EntityNameLocator.java @@ -0,0 +1,34 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed 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.springframework.batch.execution.repository.dao; + +/** + * Locator strategy for entity names based on class. + * + * @author Dave Syer + * + */ +public interface EntityNameLocator { + + /** + * Translate a concrete class into an entity name. + * + * @param clz the Class of the object to locate + * @return an entity name + */ + String locate(Class clz); + +} 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 0ec647f90..4ce2f597b 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 @@ -26,6 +26,7 @@ import org.springframework.batch.core.domain.JobIdentifier; import org.springframework.batch.core.domain.JobInstance; import org.springframework.batch.core.repository.NoSuchBatchDomainObjectException; import org.springframework.batch.execution.runtime.ScheduledJobIdentifier; +import org.springframework.beans.factory.InitializingBean; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.util.Assert; @@ -39,8 +40,28 @@ import org.springframework.util.Assert; * @author Dave Syer */ -public class HibernateJobDao extends HibernateDaoSupport implements JobDao { +public class HibernateJobDao extends HibernateDaoSupport implements JobDao, InitializingBean { + private EntityNameLocator entityNameLocator; + + /** + * Public setter for the {@link EntityNameLocator} property. + * + * @param entityNameLocator the entityNameLocator to set + */ + public void setEntityNameLocator(EntityNameLocator entityNameLocator) { + this.entityNameLocator = entityNameLocator; + } + + /** + * Check mandatory properties ({@link #entityNameLocator}). + * @see org.springframework.dao.support.DaoSupport#initDao() + */ + protected void initDao() throws Exception { + Assert.notNull(entityNameLocator, "An EntityNameLocator must be provided."); + super.initDao(); + } + /** * @see JobDao#createJob(JobIdentifier) * @@ -75,8 +96,9 @@ public class HibernateJobDao extends HibernateDaoSupport implements JobDao { List list = this.getHibernateTemplate().executeFind( new HibernateCallback() { public Object doInHibernate(Session session) { + String entityName = entityNameLocator.locate(jobRuntimeInformation.getClass()); Criteria criteria = session - .createCriteria(JobInstance.class); + .createCriteria(entityName); criteria.add(Expression.eq("identifier", jobRuntimeInformation)); return criteria.list(); diff --git a/execution/src/main/java/org/springframework/batch/execution/repository/dao/JobIdentifierEntityNameLocator.java b/execution/src/main/java/org/springframework/batch/execution/repository/dao/JobIdentifierEntityNameLocator.java new file mode 100644 index 000000000..bd3eac792 --- /dev/null +++ b/execution/src/main/java/org/springframework/batch/execution/repository/dao/JobIdentifierEntityNameLocator.java @@ -0,0 +1,131 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed 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.springframework.batch.execution.repository.dao; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.springframework.batch.core.domain.JobIdentifier; +import org.springframework.batch.core.domain.JobInstance; +import org.springframework.batch.execution.runtime.DefaultJobIdentifier; +import org.springframework.batch.execution.runtime.ScheduledJobIdentifier; +import org.springframework.util.ClassUtils; + +/** + * An {@link EntityNameLocator} that knows about {@link JobIdentifier} class + * types and translates them into entity names that are recongnized by + * Hibernate. The implementation is actually generic, and can be used as a + * general purpose {@link EntityNameLocator} by setting the + * {@link #identifierTypes} property. By default it knows about all the entity + * types that work out of the box with Spring Batch. + * + * @author Dave Syer + * + */ +public class JobIdentifierEntityNameLocator implements EntityNameLocator { + + private static final String SIMPLE_JOB_INSTANCE = "SimpleJobInstance"; + private Map identifierTypes; + private Set entrySet; + + public JobIdentifierEntityNameLocator() { + Map types = new HashMap(); + types.put(ScheduledJobIdentifier.class, "ScheduledJobInstance"); + types.put(DefaultJobIdentifier.class, "DefaultJobInstance"); + setIdentifierTypes(types); + }; + + /** + * 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); + } + } + } + + this.entrySet = new TreeSet(new ClassComparator()); + entrySet.addAll(identifierTypes.keySet()); + + } + + /** + * Identify a {@link JobInstance} entity type from the given + * {@link JobIdentifier} class. + * + * @see org.springframework.batch.execution.repository.dao.EntityNameLocator#locate(java.lang.Class) + */ + public String locate(Class clz) { + for (Iterator iterator = entrySet.iterator(); iterator.hasNext();) { + Class key = (Class) iterator.next(); + if (key.isAssignableFrom(clz)) { + return (String) identifierTypes.get(key); + } + } + return SIMPLE_JOB_INSTANCE; + } + + /** + * Comparator for classes to order by inheritance. + * + * @author Dave Syer + * + */ + private class ClassComparator implements Comparator { + /** + * @return 1 if arg0 is assignable from arg1 + * @return -1 otherwise + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + public int compare(Object arg0, Object arg1) { + Class cls0 = (Class) arg0; + Class cls1 = (Class) arg1; + if (cls0.isAssignableFrom(cls1)) { + return 1; + } + return -1; + } + } + +} diff --git a/execution/src/test/java/org/springframework/batch/execution/repository/dao/AbstractJobDaoTests.java b/execution/src/test/java/org/springframework/batch/execution/repository/dao/AbstractJobDaoTests.java index 33c1a3cba..7775c5b3d 100644 --- a/execution/src/test/java/org/springframework/batch/execution/repository/dao/AbstractJobDaoTests.java +++ b/execution/src/test/java/org/springframework/batch/execution/repository/dao/AbstractJobDaoTests.java @@ -122,6 +122,34 @@ public abstract class AbstractJobDaoTests extends } } + /** + * Test that ensures that if you create a job with a given name, then find a + * job with the same name, but other pieces of the identifier different, you + * get no result, not the existing one. + */ + public void testCreateJobWithExistingName() { + ScheduledJobIdentifier scheduledIdentifier = new ScheduledJobIdentifier( + "ScheduledJob"); + + jobDao.createJob(scheduledIdentifier); + + // Modifying the key should bring back a completely different + // JobInstance + ScheduledJobIdentifier newIdentifier = new ScheduledJobIdentifier( + "ScheduledJob"); + newIdentifier.setJobKey("different key"); + + List jobs; + jobs = jobDao.findJobs(scheduledIdentifier); + assertEquals(1, jobs.size()); + JobInstance job = (JobInstance) jobs.get(0); + assertEquals(scheduledIdentifier, job.getIdentifier()); + + jobs = jobDao.findJobs(newIdentifier); + assertEquals(0, jobs.size()); + + } + public void testUpdateJob() { // Update the returned job with a new status job.setStatus(BatchStatus.COMPLETED); 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 index 739826815..cdccc46c9 100644 --- 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 @@ -1,51 +1,29 @@ package org.springframework.batch.execution.repository.dao; -import java.util.Collections; - import junit.framework.TestCase; import org.springframework.batch.core.domain.JobInstance; -import org.springframework.batch.core.runtime.SimpleJobIdentifier; -import org.springframework.batch.execution.runtime.DefaultJobIdentifier; import org.springframework.batch.execution.runtime.ScheduledJobIdentifier; public class EntityNameInterceptorTests extends TestCase { private EntityNameInterceptor interceptor = new EntityNameInterceptor(); - public void testGetEntityNameForScheduledJobIdentifier() { - JobInstance job = new JobInstance(new ScheduledJobIdentifier("foo")); - assertEquals("ScheduledJobInstance", interceptor.getEntityName(job)); + /* (non-Javadoc) + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + interceptor.setEntityNameLocator(new EntityNameLocator() { + public String locate(Class clz) { + return "foo"; + } + }); } - - public void testGetEntityNameForDefaultJobIdentifier() { - JobInstance job = new JobInstance(new DefaultJobIdentifier("foo")); - assertEquals("DefaultJobInstance", interceptor.getEntityName(job)); - } - - public void testGetEntityNameForSimpleJobIdentifier() { - JobInstance job = new JobInstance(new SimpleJobIdentifier("foo")); - assertEquals("SimpleJobInstance", interceptor.getEntityName(job)); - } - - public void testSetIdentifierTypesWithString() { - interceptor.setIdentifierTypes(Collections.singletonMap(ScheduledJobIdentifier.class.getName(), "foo")); + + public void testGetEntityName() { JobInstance job = new JobInstance(new ScheduledJobIdentifier("foo")); assertEquals("foo", interceptor.getEntityName(job)); } - public void testSetIdentifierTypesWithClass() { - interceptor.setIdentifierTypes(Collections.singletonMap(ScheduledJobIdentifier.class, "foo")); - JobInstance job = new JobInstance(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 f75817cfb..d7a1a7792 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 @@ -60,7 +60,7 @@ public class HibernateJobDaoTests extends AbstractJobDaoTests { public void testUpdateDetachedJobExecution() { sessionFactory.getCurrentSession().evict(jobExecution); - + jobExecution.setStatus(BatchStatus.COMPLETED); jobExecution.setEndTime(new Timestamp(System.currentTimeMillis())); jobDao.update(jobExecution); diff --git a/execution/src/test/java/org/springframework/batch/execution/repository/dao/JobIdentiferEntityNameLocatorTests.java b/execution/src/test/java/org/springframework/batch/execution/repository/dao/JobIdentiferEntityNameLocatorTests.java new file mode 100644 index 000000000..63b9b9e68 --- /dev/null +++ b/execution/src/test/java/org/springframework/batch/execution/repository/dao/JobIdentiferEntityNameLocatorTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2006-2007 the original author or authors. + * + * Licensed 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.springframework.batch.execution.repository.dao; + +import java.util.Collections; + +import junit.framework.TestCase; + +import org.springframework.batch.core.runtime.SimpleJobIdentifier; +import org.springframework.batch.execution.runtime.DefaultJobIdentifier; +import org.springframework.batch.execution.runtime.ScheduledJobIdentifier; + +/** + * @author Dave Syer + * + */ +public class JobIdentiferEntityNameLocatorTests extends TestCase { + + private JobIdentifierEntityNameLocator interceptor = new JobIdentifierEntityNameLocator(); + + public void testGetEntityNameForScheduledJobIdentifier() { + assertEquals("ScheduledJobInstance", interceptor.locate(ScheduledJobIdentifier.class)); + } + + public void testGetEntityNameForDefaultJobIdentifier() { + assertEquals("DefaultJobInstance", interceptor.locate(DefaultJobIdentifier.class)); + } + + public void testGetEntityNameForSimpleJobIdentifier() { + assertEquals("SimpleJobInstance", interceptor.locate(SimpleJobIdentifier.class)); + } + + public void testSetIdentifierTypesWithString() { + interceptor.setIdentifierTypes(Collections.singletonMap(ScheduledJobIdentifier.class.getName(), "foo")); + assertEquals("foo", interceptor.locate(ScheduledJobIdentifier.class)); + } + + public void testSetIdentifierTypesWithClass() { + interceptor.setIdentifierTypes(Collections.singletonMap(ScheduledJobIdentifier.class, "foo")); + assertEquals("foo", interceptor.locate(ScheduledJobIdentifier.class)); + } + + public void testSetIdentifierTypesWithInvalidClassName() { + try { + interceptor.setIdentifierTypes(Collections.singletonMap("FooBarNotAClass", "foo")); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + +} 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 cda532b01..bbd74e1bc 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 @@ -26,12 +26,18 @@ - - - + + + + + + + - + \ No newline at end of file diff --git a/execution/src/test/resources/org/springframework/batch/execution/repository/dao/hibernate-dao-test.xml b/execution/src/test/resources/org/springframework/batch/execution/repository/dao/hibernate-dao-test.xml index af9418331..98472b15c 100644 --- a/execution/src/test/resources/org/springframework/batch/execution/repository/dao/hibernate-dao-test.xml +++ b/execution/src/test/resources/org/springframework/batch/execution/repository/dao/hibernate-dao-test.xml @@ -5,10 +5,11 @@ - + + - + \ No newline at end of file diff --git a/samples/src/main/resources/hibernate-context.xml b/samples/src/main/resources/hibernate-context.xml index 202fd8e04..0d62542d6 100644 --- a/samples/src/main/resources/hibernate-context.xml +++ b/samples/src/main/resources/hibernate-context.xml @@ -1,18 +1,28 @@ - - - - - - - classpath:/org/springframework/batch/execution/repository/dao/JobInstance.hbm.xml - classpath:/org/springframework/batch/execution/repository/dao/JobExecution.hbm.xml - classpath:/org/springframework/batch/execution/repository/dao/StepInstance.hbm.xml - classpath:/org/springframework/batch/execution/repository/dao/StepExecution.hbm.xml - - - + + + + + + + + classpath:/org/springframework/batch/execution/repository/dao/JobInstance.hbm.xml + + + classpath:/org/springframework/batch/execution/repository/dao/JobExecution.hbm.xml + + + classpath:/org/springframework/batch/execution/repository/dao/StepInstance.hbm.xml + + + classpath:/org/springframework/batch/execution/repository/dao/StepExecution.hbm.xml + + + + - + - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/src/main/resources/simple-container-definition.xml b/samples/src/main/resources/simple-container-definition.xml index 4cae0532e..55c0116f3 100644 --- a/samples/src/main/resources/simple-container-definition.xml +++ b/samples/src/main/resources/simple-container-definition.xml @@ -89,6 +89,7 @@ +