From 2da35c8f002471dfcb0e6fce3d8aec9f481282b9 Mon Sep 17 00:00:00 2001 From: dsyer Date: Mon, 18 Aug 2008 08:29:58 +0000 Subject: [PATCH] OPEN - issue BATCH-773: Refactor and extend ExportedJobLauncher to JobOperator More thorough tests and changed parameter values to long (not Long) --- .../batch/core/launch/JobOperator.java | 12 +- .../support/RuntimeExceptionTranslator.java | 41 +++++++ .../launch/support/SimpleJobOperator.java | 21 ++-- .../repository/dao/JdbcJobExecutionDao.java | 28 +++-- .../repository/dao/JdbcJobInstanceDao.java | 8 +- .../support/SimpleJobOperatorTests.java | 103 +++++++++++++++++- .../dao/AbstractJobExecutionDaoTests.java | 41 ++++++- .../dao/AbstractJobInstanceDaoTests.java | 11 ++ .../resources/adhoc-job-launcher-context.xml | 10 +- 9 files changed, 244 insertions(+), 31 deletions(-) create mode 100644 spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/RuntimeExceptionTranslator.java diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/JobOperator.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/JobOperator.java index f9a558ca0..e38f14e11 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/JobOperator.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/JobOperator.java @@ -38,18 +38,18 @@ import org.springframework.batch.core.repository.JobRestartException; */ public interface JobOperator { - List getExecutions(Long instanceId) throws NoSuchJobException; + List getExecutions(long instanceId) throws NoSuchJobInstanceException; List getLastInstances(String jobName, int count) throws NoSuchJobException; Set getRunningExecutions(String jobName) throws NoSuchJobException; - String getParameters(Long executionId) throws NoSuchJobExecutionException; + String getParameters(long executionId) throws NoSuchJobExecutionException; Long start(String jobName, String parameters) throws NoSuchJobException, JobInstanceAlreadyExistsException, JobRestartException; - Long resume(Long executionId) throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException, + Long resume(long executionId) throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException, NoSuchJobException, JobRestartException; /** @@ -73,11 +73,11 @@ public interface JobOperator { Long startNextInstance(String jobName) throws NoSuchJobException, JobParametersNotFoundException, JobRestartException, JobExecutionAlreadyRunningException, JobInstanceAlreadyCompleteException; - boolean stop(Long executionId) throws NoSuchJobExecutionException; + boolean stop(long executionId) throws NoSuchJobExecutionException; - String getSummary(Long executionId) throws NoSuchJobExecutionException; + String getSummary(long executionId) throws NoSuchJobExecutionException; - Map getStepExecutionSummaries(Long executionId) throws NoSuchJobExecutionException; + Map getStepExecutionSummaries(long executionId) throws NoSuchJobExecutionException; Set getJobNames(); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/RuntimeExceptionTranslator.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/RuntimeExceptionTranslator.java new file mode 100644 index 000000000..1321a2217 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/RuntimeExceptionTranslator.java @@ -0,0 +1,41 @@ +/* + * 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.core.launch.support; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +/** + * @author Dave Syer + * + */ +public class RuntimeExceptionTranslator implements MethodInterceptor { + + /* (non-Javadoc) + * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) + */ + public Object invoke(MethodInvocation invocation) throws Throwable { + try { + return invocation.proceed(); + } catch (Exception e) { + if (e.getClass().getName().startsWith("java")) { + throw e; + } + throw new RuntimeException(e.getClass().getSimpleName()+ ": " + e.getMessage()); + } + } + +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java index c008340b0..4178deb7c 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java @@ -42,6 +42,7 @@ import org.springframework.batch.core.launch.JobOperator; import org.springframework.batch.core.launch.JobParametersNotFoundException; import org.springframework.batch.core.launch.NoSuchJobException; import org.springframework.batch.core.launch.NoSuchJobExecutionException; +import org.springframework.batch.core.launch.NoSuchJobInstanceException; import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; import org.springframework.batch.core.repository.JobRestartException; @@ -121,10 +122,10 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * org.springframework.batch.core.launch.JobOperator#getExecutions(java. * lang.Long) */ - public List getExecutions(Long instanceId) throws NoSuchJobException { + public List getExecutions(long instanceId) throws NoSuchJobInstanceException { JobInstance jobInstance = jobExplorer.getJobInstance(instanceId); if (jobInstance == null) { - throw new NoSuchJobException(String.format("No job instance with id=%d", instanceId)); + throw new NoSuchJobInstanceException(String.format("No job instance with id=%d", instanceId)); } List list = new ArrayList(); for (JobExecution jobExecution : jobExplorer.findJobExecutions(jobInstance)) { @@ -154,6 +155,9 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { for (JobInstance jobInstance : jobExplorer.getLastJobInstances(jobName, count)) { list.add(jobInstance.getId()); } + if (list.isEmpty() && !jobRegistry.getJobNames().contains(jobName)) { + throw new NoSuchJobException("No such job (either in registry or in historical data): "+jobName); + } return list; } @@ -164,7 +168,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * org.springframework.batch.core.launch.JobOperator#getParameters(java. * lang.Long) */ - public String getParameters(Long executionId) throws NoSuchJobExecutionException { + public String getParameters(long executionId) throws NoSuchJobExecutionException { JobExecution jobExecution = jobExplorer.getJobExecution(executionId); if (jobExecution == null) { throw new NoSuchJobExecutionException(String.format("No job execution with id=%d", executionId)); @@ -185,6 +189,9 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { for (JobExecution jobExecution : jobExplorer.findRunningJobExecutions(jobName)) { set.add(jobExecution.getId()); } + if (set.isEmpty() && !jobRegistry.getJobNames().contains(jobName)) { + throw new NoSuchJobException("No such job (either in registry or in historical data): "+jobName); + } return set; } @@ -195,7 +202,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * org.springframework.batch.core.launch.JobOperator#getStepExecutionSummaries * (java.lang.Long) */ - public Map getStepExecutionSummaries(Long executionId) throws NoSuchJobExecutionException { + public Map getStepExecutionSummaries(long executionId) throws NoSuchJobExecutionException { JobExecution jobExecution = jobExplorer.getJobExecution(executionId); if (jobExecution == null) { throw new NoSuchJobExecutionException(String.format("No job execution with id=%d", executionId)); @@ -214,7 +221,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * org.springframework.batch.core.launch.JobOperator#getSummary(java.lang * .Long) */ - public String getSummary(Long executionId) throws NoSuchJobExecutionException { + public String getSummary(long executionId) throws NoSuchJobExecutionException { JobExecution jobExecution = jobExplorer.getJobExecution(executionId); if (jobExecution == null) { throw new NoSuchJobExecutionException(String.format("No job execution with id=%d", executionId)); @@ -228,7 +235,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * @see * org.springframework.batch.core.launch.JobOperator#resume(java.lang.Long) */ - public Long resume(Long executionId) throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException, + public Long resume(long executionId) throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException, NoSuchJobException, JobRestartException { logger.info("Checking status of job execution with id=" + executionId); @@ -348,7 +355,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * @see * org.springframework.batch.core.launch.JobOperator#stop(java.lang.Long) */ - public boolean stop(Long executionId) throws NoSuchJobExecutionException { + public boolean stop(long executionId) throws NoSuchJobExecutionException { throw new UnsupportedOperationException("See BATCH-453 for implementation."); } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java index 35c4309d4..d315d0ddd 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java @@ -14,6 +14,7 @@ import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; import org.springframework.batch.repeat.ExitStatus; import org.springframework.beans.factory.InitializingBean; +import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.RowCallbackHandler; import org.springframework.jdbc.core.simple.ParameterizedRowMapper; import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer; @@ -224,20 +225,29 @@ public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements /* * (non-Javadoc) - * @see org.springframework.batch.core.repository.dao.JobExecutionDao#getLastJobExecution(java.lang.String) + * + * @seeorg.springframework.batch.core.repository.dao.JobExecutionDao# + * getLastJobExecution(java.lang.String) */ public JobExecution getJobExecution(Long executionId) { - Long instanceId = getJdbcTemplate().queryForLong(getQuery(GET_INSTANCE_BY_EXECUTION_ID), executionId); - JobInstance jobInstance = jobInstanceDao.getJobInstance(instanceId); - JobExecution jobExecution = getJdbcTemplate().queryForObject(getQuery(GET_EXECUTION_BY_ID), new JobExecutionRowMapper(jobInstance), - executionId); - stepExecutionDao.getStepExecutions(jobExecution); - return jobExecution; + try { + Long instanceId = getJdbcTemplate().queryForLong(getQuery(GET_INSTANCE_BY_EXECUTION_ID), executionId); + JobInstance jobInstance = jobInstanceDao.getJobInstance(instanceId); + JobExecution jobExecution = getJdbcTemplate().queryForObject(getQuery(GET_EXECUTION_BY_ID), + new JobExecutionRowMapper(jobInstance), executionId); + stepExecutionDao.getStepExecutions(jobExecution); + return jobExecution; + } + catch (EmptyResultDataAccessException e) { + return null; + } } /* * (non-Javadoc) - * @see org.springframework.batch.core.repository.dao.JobExecutionDao#findRunningJobExecutions(java.lang.String) + * + * @seeorg.springframework.batch.core.repository.dao.JobExecutionDao# + * findRunningJobExecutions(java.lang.String) */ public Set findRunningJobExecutions(String jobName) { @@ -251,7 +261,7 @@ public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements } }; getJdbcTemplate().getJdbcOperations().query(getQuery(GET_RUNNING_EXECUTIONS), handler); - + for (JobExecution jobExecution : result) { stepExecutionDao.getStepExecutions(jobExecution); } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobInstanceDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobInstanceDao.java index 9fcef875b..cb584d42d 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobInstanceDao.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobInstanceDao.java @@ -16,6 +16,7 @@ import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParameter.ParameterType; import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowCallbackHandler; import org.springframework.jdbc.core.simple.ParameterizedRowMapper; @@ -183,7 +184,12 @@ public class JdbcJobInstanceDao extends AbstractJdbcBatchMetadataDao implements final JobParameters jobParameters = getJobParameters(instanceId); ParameterizedRowMapper rowMapper = new JobInstanceRowMapper(jobParameters); - return getJdbcTemplate().queryForObject(getQuery(GET_JOB_FROM_ID), rowMapper, instanceId); + try { + return getJdbcTemplate().queryForObject(getQuery(GET_JOB_FROM_ID), rowMapper, instanceId); + } + catch (EmptyResultDataAccessException e) { + return null; + } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java index c5d97cb78..98ef7196b 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java @@ -42,6 +42,8 @@ import org.springframework.batch.core.job.JobSupport; import org.springframework.batch.core.launch.JobInstanceAlreadyExistsException; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.launch.NoSuchJobException; +import org.springframework.batch.core.launch.NoSuchJobExecutionException; +import org.springframework.batch.core.launch.NoSuchJobInstanceException; import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; import org.springframework.batch.core.repository.JobRestartException; @@ -89,9 +91,10 @@ public class SimpleJobOperatorTests { } throw new NoSuchJobException("foo"); } + @Override public Collection getJobNames() { - return Arrays.asList(new String[] {"foo", "bar"}); + return Arrays.asList(new String[] { "foo", "bar" }); } }); @@ -214,6 +217,21 @@ public class SimpleJobOperatorTests { EasyMock.verify(jobExplorer); } + @Test + public void testGetSummaryNoSuchExecution() throws Exception { + jobParameters = new JobParameters(); + jobExplorer.getJobExecution(111L); + EasyMock.expectLastCall().andReturn(null); + EasyMock.replay(jobExplorer); + try { + jobOperator.getSummary(111L); + fail("Expected NoSuchJobExecutionException"); + } catch (NoSuchJobExecutionException e) { + // expected + } + EasyMock.verify(jobExplorer); + } + @Test public void testGetStepExecutionSummariesSunnyDay() throws Exception { jobParameters = new JobParameters(); @@ -229,6 +247,21 @@ public class SimpleJobOperatorTests { EasyMock.verify(jobExplorer); } + @Test + public void testGetStepExecutionSummariesNoSuchExecution() throws Exception { + jobParameters = new JobParameters(); + jobExplorer.getJobExecution(111L); + EasyMock.expectLastCall().andReturn(null); + EasyMock.replay(jobExplorer); + try { + jobOperator.getStepExecutionSummaries(111L); + fail("Expected NoSuchJobExecutionException"); + } catch (NoSuchJobExecutionException e) { + // expected + } + EasyMock.verify(jobExplorer); + } + @Test public void testFindRunningExecutionsSunnyDay() throws Exception { jobParameters = new JobParameters(); @@ -241,6 +274,21 @@ public class SimpleJobOperatorTests { EasyMock.verify(jobExplorer); } + @Test + public void testFindRunningExecutionsNoSuchJob() throws Exception { + jobParameters = new JobParameters(); + jobExplorer.findRunningJobExecutions("no-such-job"); + EasyMock.expectLastCall().andReturn(Collections.emptySet()); + EasyMock.replay(jobExplorer); + try { + jobOperator.getRunningExecutions("no-such-job"); + fail("Expected NoSuchJobException"); + } catch (NoSuchJobException e) { + // expected + } + EasyMock.verify(jobExplorer); + } + @Test public void testGetJobParametersSunnyDay() throws Exception { final JobParameters jobParameters = new JobParameters(); @@ -253,23 +301,53 @@ public class SimpleJobOperatorTests { EasyMock.verify(jobExplorer); } + @Test + public void testGetJobParametersNoSuchExecution() throws Exception { + jobExplorer.getJobExecution(111L); + EasyMock.expectLastCall().andReturn(null); + EasyMock.replay(jobExplorer); + try { + jobOperator.getParameters(111L); + fail("Expected NoSuchJobExecutionException"); + } catch (NoSuchJobExecutionException e) { + // expected + } + EasyMock.verify(jobExplorer); + } + @Test public void testGetLastInstancesSunnyDay() throws Exception { jobParameters = new JobParameters(); - jobExplorer.getLastJobInstances("foo",2); + jobExplorer.getLastJobInstances("foo", 2); JobInstance jobInstance = new JobInstance(123L, jobParameters, job.getName()); EasyMock.expectLastCall().andReturn(Collections.singletonList(jobInstance)); EasyMock.replay(jobExplorer); - List value = jobOperator.getLastInstances("foo",2); + List value = jobOperator.getLastInstances("foo", 2); assertEquals(123L, value.get(0).longValue()); EasyMock.verify(jobExplorer); } - + + @Test + public void testGetLastInstancesNoSuchJob() throws Exception { + jobParameters = new JobParameters(); + jobExplorer.getLastJobInstances("no-such-job", 2); + EasyMock.expectLastCall().andReturn(Collections.emptyList()); + EasyMock.replay(jobExplorer); + try { + jobOperator.getLastInstances("no-such-job", 2); + fail("Expected NoSuchJobException"); + } + catch (NoSuchJobException e) { + // expected + } + EasyMock.verify(jobExplorer); + } + @Test public void testGetJobNames() throws Exception { Set names = jobOperator.getJobNames(); assertEquals(2, names.size()); - assertTrue("Wrong names: "+names, names.contains("foo")); + assertTrue("Wrong names: " + names, names.contains("foo")); } @Test @@ -286,4 +364,19 @@ public class SimpleJobOperatorTests { EasyMock.verify(jobExplorer); } + @Test + public void testGetExecutionsNoSuchInstance() throws Exception { + jobExplorer.getJobInstance(123L); + EasyMock.expectLastCall().andReturn(null); + EasyMock.replay(jobExplorer); + try { + jobOperator.getExecutions(123L); + fail("Expected NoSuchJobInstanceException"); + } + catch (NoSuchJobInstanceException e) { + // expected + } + EasyMock.verify(jobExplorer); + } + } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java index 53bcb7a43..733f97b4f 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java @@ -52,10 +52,20 @@ public abstract class AbstractJobExecutionDaoTests extends AbstractTransactional dao.saveJobExecution(execution); List executions = dao.findJobExecutions(jobInstance); - assertTrue(executions.size() == 1); + assertEquals(1, executions.size()); assertEquals(execution, executions.get(0)); } + /** + * Save and find a job execution. + */ + @Transactional + @Test + public void testFindNonExistentExecutions() { + List executions = dao.findJobExecutions(jobInstance); + assertEquals(0, executions.size()); + } + /** * Saving sets id to the entity. */ @@ -107,6 +117,16 @@ public abstract class AbstractJobExecutionDaoTests extends AbstractTransactional assertEquals(exec2, last); } + /** + * Check the execution is returned + */ + @Transactional + @Test + public void testGetMissingLastExecution() { + JobExecution value = dao.getLastJobExecution(jobInstance); + assertNull(value); + } + /** * Check the execution is returned */ @@ -134,6 +154,16 @@ public abstract class AbstractJobExecutionDaoTests extends AbstractTransactional assertEquals(1, value.getStepExecutions().size()); } + /** + * Check the execution is returned + */ + @Transactional + @Test + public void testNoRunningExecutions() { + Set values = dao.findRunningJobExecutions("no-such-job"); + assertEquals(0, values.size()); + } + /** * Check the execution is returned */ @@ -157,4 +187,13 @@ public abstract class AbstractJobExecutionDaoTests extends AbstractTransactional assertEquals(1, value.getStepExecutions().size()); } + /** + * Check the execution is returned + */ + @Transactional + @Test + public void testGetMissingExecution() { + JobExecution value = dao.getJobExecution(54321L); + assertNull(value); + } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobInstanceDaoTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobInstanceDaoTests.java index debde7016..a8611eb3f 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobInstanceDaoTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobInstanceDaoTests.java @@ -83,6 +83,17 @@ public abstract class AbstractJobInstanceDaoTests extends AbstractTransactionalJ assertEquals(new Date(DATE), retrievedParams.getDate("dateKey")); } + /* + * Create and retrieve a job instance. + */ + @Transactional @Test + public void testGetMissingById() throws Exception { + + JobInstance retrievedInstance = dao.getJobInstance(1111111L); + assertNull(retrievedInstance); + + } + /* * Create and retrieve a job instance. */ diff --git a/spring-batch-samples/src/main/resources/adhoc-job-launcher-context.xml b/spring-batch-samples/src/main/resources/adhoc-job-launcher-context.xml index a4b09c5d6..97f42d2e4 100644 --- a/spring-batch-samples/src/main/resources/adhoc-job-launcher-context.xml +++ b/spring-batch-samples/src/main/resources/adhoc-job-launcher-context.xml @@ -9,7 +9,12 @@ - + + + + + + @@ -18,7 +23,7 @@ - + @@ -43,6 +48,7 @@ +