OPEN - issue BATCH-791: Ditch Tasklet (StepHandler is more flexible)
Done. Use StepHandler instead.
This commit is contained in:
@@ -13,22 +13,24 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.core.step.tasklet;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Adapts a {@link Callable}<{@link ExitStatus}> to the
|
||||
* {@link Tasklet} interface.
|
||||
* {@link StepHandler} interface.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class CallableTaskletAdapter implements Tasklet, InitializingBean {
|
||||
public class CallableStepHandlerAdapter implements StepHandler, InitializingBean {
|
||||
|
||||
private Callable<ExitStatus> callable;
|
||||
|
||||
@@ -50,10 +52,11 @@ public class CallableTaskletAdapter implements Tasklet, InitializingBean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the provided Callable and return its {@link ExitStatus}.
|
||||
* @see org.springframework.batch.core.step.tasklet.Tasklet#execute()
|
||||
* Execute the provided Callable and return its {@link ExitStatus}. Ignores
|
||||
* the {@link StepContribution} and the attributes.
|
||||
* @see StepHandler#handle(StepContribution, AttributeAccessor)
|
||||
*/
|
||||
public ExitStatus execute() throws Exception {
|
||||
public ExitStatus handle(StepContribution contribution, AttributeAccessor attributes) throws Exception {
|
||||
return callable.call();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.batch.sample.common;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.batch.sample.common;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.core.step.item;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
@@ -13,15 +13,17 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.core.step.tasklet;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.item.adapter.AbstractMethodInvokingDelegator;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
|
||||
/**
|
||||
* A {@link Tasklet} that wraps a method in a POJO. By default the return value
|
||||
* is {@link ExitStatus#FINISHED} unless the delegate POJO itself returns an
|
||||
* {@link ExitStatus}. The POJO method is usually going to have no arguments,
|
||||
* A {@link StepHandler} that wraps a method in a POJO. By default the return
|
||||
* value is {@link ExitStatus#FINISHED} unless the delegate POJO itself returns
|
||||
* an {@link ExitStatus}. The POJO method is usually going to have no arguments,
|
||||
* but a static argument or array of arguments can be used by setting the
|
||||
* arguments property.
|
||||
*
|
||||
@@ -30,15 +32,16 @@ import org.springframework.batch.repeat.ExitStatus;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class TaskletAdapter extends AbstractMethodInvokingDelegator<Object> implements Tasklet {
|
||||
public class StepHandlerAdapter extends AbstractMethodInvokingDelegator<Object> implements StepHandler {
|
||||
|
||||
/**
|
||||
* Delegate execution to the target object and translate the return value to
|
||||
* an {@link ExitStatus} by invoking a method in the delegate POJO.
|
||||
* an {@link ExitStatus} by invoking a method in the delegate POJO. Ignores
|
||||
* the {@link StepContribution} and the attributes.
|
||||
*
|
||||
* @see org.springframework.batch.core.step.tasklet.Tasklet#execute()
|
||||
* @see StepHandler#handle(StepContribution, AttributeAccessor)
|
||||
*/
|
||||
public ExitStatus execute() throws Exception {
|
||||
public ExitStatus handle(StepContribution contribution, AttributeAccessor attributes) throws Exception {
|
||||
return mapResult(invokeDelegateMethod());
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.core.step.item;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
@@ -79,7 +79,7 @@ public class StepHandlerStep extends AbstractStep {
|
||||
|
||||
private TransactionAttribute transactionAttribute = new DefaultTransactionAttribute();
|
||||
|
||||
private StepHandler itemHandler;
|
||||
private StepHandler stepHandler;
|
||||
|
||||
private StepExecutionSynchronizer synchronizer;
|
||||
|
||||
@@ -111,10 +111,10 @@ public class StepHandlerStep extends AbstractStep {
|
||||
/**
|
||||
* Public setter for the {@link StepHandler}.
|
||||
*
|
||||
* @param itemHandler the {@link StepHandler} to set
|
||||
* @param stepHandler the {@link StepHandler} to set
|
||||
*/
|
||||
public void setStepHandler(StepHandler itemHandler) {
|
||||
this.itemHandler = itemHandler;
|
||||
public void setStepHandler(StepHandler stepHandler) {
|
||||
this.stepHandler = stepHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,7 +239,7 @@ public class StepHandlerStep extends AbstractStep {
|
||||
try {
|
||||
|
||||
try {
|
||||
exitStatus = itemHandler.handle(contribution, attributes);
|
||||
exitStatus = stepHandler.handle(contribution, attributes);
|
||||
}
|
||||
catch (Error e) {
|
||||
if (transactionAttribute.rollbackOn(e)) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.batch.sample.common;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
/**
|
||||
* Exception indicating failed execution of system command.
|
||||
@@ -1,19 +1,20 @@
|
||||
package org.springframework.batch.sample.common;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.lang.time.StopWatch;
|
||||
import org.springframework.batch.core.JobInterruptedException;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Tasklet} that executes a system command.
|
||||
* {@link StepHandler} that executes a system command.
|
||||
*
|
||||
* The system command is executed in a new thread - timeout value is required to
|
||||
* be set, so that the batch job does not hang forever if the external process
|
||||
@@ -30,7 +31,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class SystemCommandTasklet extends StepExecutionListenerSupport implements Tasklet, InitializingBean {
|
||||
public class SystemCommandStepHandler extends StepExecutionListenerSupport implements StepHandler, InitializingBean {
|
||||
|
||||
private String command;
|
||||
|
||||
@@ -45,12 +46,12 @@ public class SystemCommandTasklet extends StepExecutionListenerSupport implement
|
||||
private long checkInterval = 1000;
|
||||
|
||||
private StepExecution execution = null;
|
||||
|
||||
|
||||
/**
|
||||
* Execute system command and map its exit code to {@link ExitStatus} using
|
||||
* {@link SystemProcessExitCodeMapper}.
|
||||
*/
|
||||
public ExitStatus execute() throws Exception {
|
||||
public ExitStatus handle(StepContribution contribution, AttributeAccessor attributes) throws Exception {
|
||||
ExecutorThread executorThread = new ExecutorThread();
|
||||
executorThread.start();
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package org.springframework.batch.sample.common;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import org.springframework.batch.core.step.handler.SystemCommandStepHandler;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
|
||||
/**
|
||||
* Maps the exit code of a system process to ExitStatus value
|
||||
* returned by a system command. Designed for use with the
|
||||
* {@link SystemCommandTasklet}.
|
||||
* {@link SystemCommandStepHandler}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.step.handler.StepHandler;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
@@ -79,7 +80,7 @@ public class ItemOrientedStepHandler<T, S> implements StepHandler {
|
||||
* {@link ItemProcessor} returns null, the write is omitted and another item
|
||||
* taken from the reader.
|
||||
*
|
||||
* @see org.springframework.batch.core.step.item.StepHandler#handle(org.springframework.batch.core.StepContribution,
|
||||
* @see org.springframework.batch.core.step.handler.StepHandler#handle(org.springframework.batch.core.StepContribution,
|
||||
* AttributeAccessor)
|
||||
*/
|
||||
public ExitStatus handle(final StepContribution contribution, AttributeAccessor attributes) throws Exception {
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.step.handler.StepHandler;
|
||||
import org.springframework.batch.core.step.handler.StepHandlerStep;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.List;
|
||||
import org.springframework.batch.core.SkipListener;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.listener.CompositeSkipListener;
|
||||
import org.springframework.batch.core.step.handler.StepHandlerStep;
|
||||
import org.springframework.batch.core.step.skip.ItemSkipPolicy;
|
||||
import org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy;
|
||||
import org.springframework.batch.core.step.skip.SkipLimitExceededException;
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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.step.tasklet;
|
||||
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
|
||||
/**
|
||||
* Interface for encapsulating processing logic that is not natural to split
|
||||
* into read-(transform)-write phases, such as invoking a system command or a
|
||||
* stored procedure.<br/>
|
||||
*
|
||||
* Since the batch framework has no visibility inside the {@link #execute()}
|
||||
* method, developers should consider implementing {@link StepExecutionListener} and
|
||||
* check the {@link StepExecution#isTerminateOnly()} value for long lasting
|
||||
* processes to enable prompt termination of processing on user request.<br/>
|
||||
*
|
||||
* It is expected the read-(transform)-write separation will be appropriate for
|
||||
* most cases and developers should implement {@link ItemReader} and
|
||||
* {@link ItemWriter} interfaces then (typically extending or composing provided
|
||||
* implementations).<br/>
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Dave Syer
|
||||
* @author Robert Kasanicky
|
||||
*
|
||||
*/
|
||||
public interface Tasklet {
|
||||
|
||||
/**
|
||||
* Encapsulates execution logic of {@link Step}, which is unnatural to
|
||||
* separate into read-(transform)-write phases.
|
||||
*
|
||||
* @return ExitStatus indicating success or failure
|
||||
* @see org.springframework.batch.repeat.ExitStatus
|
||||
*/
|
||||
public ExitStatus execute() throws Exception;
|
||||
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* 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.step.tasklet;
|
||||
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.step.AbstractStep;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link Step} that executes a {@link Tasklet} directly. This step does not
|
||||
* manage transactions or any looping functionality. The tasklet should do this
|
||||
* on its own.
|
||||
*
|
||||
* If the {@link Tasklet} itself implements {@link StepExecutionListener} it
|
||||
* will be registered automatically, but its injected dependencies will not be.
|
||||
* This is a good way to get access to job parameters and execution context if
|
||||
* the tasklet is parameterized.
|
||||
*
|
||||
* @author Ben Hale
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class TaskletStep extends AbstractStep {
|
||||
|
||||
private Tasklet tasklet;
|
||||
|
||||
/**
|
||||
* Register each of the objects as listeners.
|
||||
*
|
||||
* @deprecated use
|
||||
* {@link #setStepExecutionListeners(StepExecutionListener[])} instead
|
||||
*/
|
||||
public void setStepListeners(StepExecutionListener[] listeners) {
|
||||
setStepExecutionListeners(listeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check mandatory properties.
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
super.afterPropertiesSet();
|
||||
Assert.notNull(tasklet, "Tasklet is mandatory for TaskletStep");
|
||||
if (tasklet instanceof StepExecutionListener) {
|
||||
registerStepExecutionListener((StepExecutionListener) tasklet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor is useful for XML configuration.
|
||||
*/
|
||||
public TaskletStep() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>Step</code> for executing a <code>Tasklet</code>
|
||||
*
|
||||
* @param tasklet The <code>Tasklet</code> to execute
|
||||
* @param jobRepository The <code>JobRepository</code> to use for
|
||||
* persistence of incremental state
|
||||
*/
|
||||
public TaskletStep(Tasklet tasklet, JobRepository jobRepository) {
|
||||
this();
|
||||
this.tasklet = tasklet;
|
||||
setJobRepository(jobRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the {@link Tasklet}.
|
||||
* @param tasklet the {@link Tasklet} to set
|
||||
*/
|
||||
public void setTasklet(Tasklet tasklet) {
|
||||
this.tasklet = tasklet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate to tasklet.
|
||||
*/
|
||||
protected ExitStatus doExecute(StepExecution stepExecution) throws Exception {
|
||||
return tasklet.execute();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.core.step.tasklet;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
@@ -22,22 +22,19 @@ import java.util.concurrent.Callable;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
public class CallableTaskletAdapterTests {
|
||||
|
||||
private CallableTaskletAdapter adapter = new CallableTaskletAdapter();
|
||||
|
||||
/**
|
||||
* Test method for {@link org.springframework.batch.core.step.tasklet.CallableTaskletAdapter#execute()}.
|
||||
* @throws Exception
|
||||
*/
|
||||
public class CallableStepHandlerAdapterTests {
|
||||
|
||||
private CallableStepHandlerAdapter adapter = new CallableStepHandlerAdapter();
|
||||
|
||||
@Test
|
||||
public void testExecute() throws Exception {
|
||||
public void testHandle() throws Exception {
|
||||
adapter.setCallable(new Callable<ExitStatus>() {
|
||||
public ExitStatus call() throws Exception {
|
||||
return ExitStatus.FINISHED;
|
||||
}
|
||||
});
|
||||
assertEquals(ExitStatus.FINISHED, adapter.execute());
|
||||
assertEquals(ExitStatus.FINISHED, adapter.handle(null,null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.batch.sample.common;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.fail;
|
||||
@@ -7,6 +7,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.step.handler.ConfigurableSystemProcessExitCodeMapper;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
|
||||
/**
|
||||
@@ -13,8 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.core.step.item;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import org.springframework.batch.core.step.handler.StepHandler;
|
||||
import org.springframework.batch.core.step.item.ItemOrientedStepHandler;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.item.support.PassthroughItemProcessor;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.batch.sample.common;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.core.step.item;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.batch.core.repository.dao.MapJobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.MapStepExecutionDao;
|
||||
import org.springframework.batch.core.repository.support.SimpleJobRepository;
|
||||
import org.springframework.batch.core.step.StepExecutionSynchronizer;
|
||||
import org.springframework.batch.core.step.handler.StepHandlerStep;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.core.step.tasklet;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -25,9 +25,9 @@ import org.springframework.batch.repeat.ExitStatus;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class TaskletAdapterTests {
|
||||
public class StepHandlerAdapterTests {
|
||||
|
||||
private TaskletAdapter tasklet = new TaskletAdapter();
|
||||
private StepHandlerAdapter tasklet = new StepHandlerAdapter();
|
||||
private Object result = null;
|
||||
|
||||
public ExitStatus execute() {
|
||||
@@ -38,41 +38,28 @@ public class TaskletAdapterTests {
|
||||
return result ;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see junit.framework.TestCase#setUp()
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
tasklet.setTargetObject(this);
|
||||
tasklet.setTargetMethod("execute");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.springframework.batch.core.step.tasklet.TaskletAdapter#execute()}.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testExecuteWithExitStatus() throws Exception {
|
||||
assertEquals(ExitStatus.NOOP, tasklet.execute());
|
||||
tasklet.setTargetMethod("execute");
|
||||
assertEquals(ExitStatus.NOOP, tasklet.handle(null,null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.springframework.batch.core.step.tasklet.TaskletAdapter#mapResult(java.lang.Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testMapResultWithNull() throws Exception {
|
||||
tasklet.setTargetMethod("process");
|
||||
assertEquals(ExitStatus.FINISHED, tasklet.execute());
|
||||
assertEquals(ExitStatus.FINISHED, tasklet.handle(null,null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.springframework.batch.core.step.tasklet.TaskletAdapter#mapResult(java.lang.Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testMapResultWithNonNull() throws Exception {
|
||||
tasklet.setTargetMethod("process");
|
||||
this.result = "foo";
|
||||
assertEquals(ExitStatus.FINISHED, tasklet.execute());
|
||||
assertEquals(ExitStatus.FINISHED, tasklet.handle(null,null));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.core.step.item;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -39,6 +39,7 @@ import org.springframework.batch.core.repository.dao.MapJobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.MapJobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.MapStepExecutionDao;
|
||||
import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
|
||||
import org.springframework.batch.core.step.handler.StepHandlerStep;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
@@ -14,15 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.core.step.item;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
@@ -42,6 +47,7 @@ import org.springframework.batch.core.repository.dao.MapStepExecutionDao;
|
||||
import org.springframework.batch.core.repository.support.SimpleJobRepository;
|
||||
import org.springframework.batch.core.step.JobRepositorySupport;
|
||||
import org.springframework.batch.core.step.StepInterruptionPolicy;
|
||||
import org.springframework.batch.core.step.handler.StepHandlerStep;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
@@ -58,7 +64,7 @@ import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.support.DefaultTransactionStatus;
|
||||
|
||||
public class StepHandlerStepTests extends TestCase {
|
||||
public class StepHandlerStepTests {
|
||||
|
||||
List<String> processed = new ArrayList<String>();
|
||||
|
||||
@@ -99,7 +105,8 @@ public class StepHandlerStepTests extends TestCase {
|
||||
return step;
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MapJobInstanceDao.clear();
|
||||
MapStepExecutionDao.clear();
|
||||
MapJobExecutionDao.clear();
|
||||
@@ -119,6 +126,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStepExecutor() throws Exception {
|
||||
|
||||
JobExecution jobExecutionContext = new JobExecution(jobInstance);
|
||||
@@ -133,6 +141,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
/**
|
||||
* StepExecution should be updated after every chunk commit.
|
||||
*/
|
||||
@Test
|
||||
public void testStepExecutionUpdates() throws Exception {
|
||||
|
||||
JobExecution jobExecution = new JobExecution(jobInstance);
|
||||
@@ -153,6 +162,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
/**
|
||||
* Failure to update StepExecution after chunk commit is fatal.
|
||||
*/
|
||||
@Test
|
||||
public void testStepExecutionUpdateFailure() throws Exception {
|
||||
|
||||
JobExecution jobExecution = new JobExecution(jobInstance);
|
||||
@@ -175,6 +185,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRepository() throws Exception {
|
||||
|
||||
SimpleJobRepository repository = new SimpleJobRepository(new MapJobInstanceDao(), new MapJobExecutionDao(),
|
||||
@@ -188,6 +199,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
assertEquals(1, processed.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncrementRollbackCount() {
|
||||
|
||||
ItemReader<String> itemReader = new ItemReader<String>() {
|
||||
@@ -211,6 +223,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExitCodeDefaultClassification() throws Exception {
|
||||
|
||||
ItemReader<String> itemReader = new ItemReader<String>() {
|
||||
@@ -235,6 +248,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExitCodeCustomClassification() throws Exception {
|
||||
|
||||
ItemReader<String> itemReader = new ItemReader<String>() {
|
||||
@@ -270,6 +284,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
* make sure a job that has never been executed before, but does have
|
||||
* saveExecutionAttributes = true, doesn't have restoreFrom called on it.
|
||||
*/
|
||||
@Test
|
||||
public void testNonRestartedJob() throws Exception {
|
||||
MockRestartableItemReader tasklet = new MockRestartableItemReader();
|
||||
step.setStepHandler(new SimpleStepHandler<String>(tasklet, itemWriter));
|
||||
@@ -283,6 +298,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
assertTrue(tasklet.isGetExecutionAttributesCalled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulExecutionWithExecutionContext() throws Exception {
|
||||
final JobExecution jobExecution = new JobExecution(jobInstance);
|
||||
final StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
|
||||
@@ -299,6 +315,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
assertEquals(3, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulExecutionWithFailureOnSaveOfExecutionContext() throws Exception {
|
||||
final JobExecution jobExecution = new JobExecution(jobInstance);
|
||||
final StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
|
||||
@@ -328,6 +345,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
* set to false, doesn't have restore or getExecutionAttributes called on
|
||||
* it.
|
||||
*/
|
||||
@Test
|
||||
public void testNoSaveExecutionAttributesRestartableJob() {
|
||||
MockRestartableItemReader tasklet = new MockRestartableItemReader();
|
||||
step.setStepHandler(new SimpleStepHandler<String>(tasklet, itemWriter));
|
||||
@@ -349,6 +367,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
* nothing will be restored because the Tasklet does not implement
|
||||
* Restartable.
|
||||
*/
|
||||
@Test
|
||||
public void testRestartJobOnNonRestartableTasklet() throws Exception {
|
||||
step.setStepHandler(new SimpleStepHandler<String>(new ItemReader<String>() {
|
||||
public String read() throws Exception {
|
||||
@@ -361,6 +380,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
step.execute(stepExecution);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreamManager() throws Exception {
|
||||
MockRestartableItemReader reader = new MockRestartableItemReader() {
|
||||
public String read() throws Exception {
|
||||
@@ -385,6 +405,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
assertEquals("bar", stepExecution.getExecutionContext().getString("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDirectlyInjectedItemStream() throws Exception {
|
||||
step.setStreams(new ItemStream[] { new ItemStreamSupport() {
|
||||
public void update(ExecutionContext executionContext) {
|
||||
@@ -401,6 +422,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
assertEquals("bar", stepExecution.getExecutionContext().getString("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDirectlyInjectedListener() throws Exception {
|
||||
step.registerStepExecutionListener(new StepExecutionListenerSupport() {
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
@@ -418,6 +440,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
assertEquals(2, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListenerCalledBeforeStreamOpened() throws Exception {
|
||||
MockRestartableItemReader reader = new MockRestartableItemReader() {
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
@@ -435,6 +458,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
assertEquals(1, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAfterStep() throws Exception {
|
||||
|
||||
final ExitStatus customStatus = new ExitStatus(false, "custom code");
|
||||
@@ -459,6 +483,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
assertEquals(customStatus.getExitDescription(), returnedStatus.getExitDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDirectlyInjectedListenerOnError() throws Exception {
|
||||
step.registerStepExecutionListener(new StepExecutionListenerSupport() {
|
||||
public ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e) {
|
||||
@@ -483,6 +508,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
assertEquals(1, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDirectlyInjectedStreamWhichIsAlsoReader() throws Exception {
|
||||
MockRestartableItemReader reader = new MockRestartableItemReader() {
|
||||
public String read() throws Exception {
|
||||
@@ -507,6 +533,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
assertEquals("bar", stepExecution.getExecutionContext().getString("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusForInterruptedException() {
|
||||
|
||||
StepInterruptionPolicy interruptionPolicy = new StepInterruptionPolicy() {
|
||||
@@ -546,6 +573,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusForNormalFailure() throws Exception {
|
||||
|
||||
ItemReader<String> itemReader = new ItemReader<String>() {
|
||||
@@ -573,6 +601,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusForErrorFailure() throws Exception {
|
||||
|
||||
ItemReader<String> itemReader = new ItemReader<String>() {
|
||||
@@ -600,6 +629,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusForResetFailedException() throws Exception {
|
||||
|
||||
ItemReader<String> itemReader = new ItemReader<String>() {
|
||||
@@ -635,6 +665,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusForCommitFailedException() throws Exception {
|
||||
|
||||
step.setTransactionManager(new ResourcelessTransactionManager() {
|
||||
@@ -665,6 +696,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusForFinalUpdateFailedException() throws Exception {
|
||||
|
||||
step.setJobRepository(new JobRepositorySupport());
|
||||
@@ -693,6 +725,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusForCloseFailedException() throws Exception {
|
||||
|
||||
MockRestartableItemReader itemReader = new MockRestartableItemReader() {
|
||||
@@ -732,6 +765,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
* commiting first chunk - otherwise ItemStreams won't recognize it is
|
||||
* restart scenario on next run.
|
||||
*/
|
||||
@Test
|
||||
public void testRestartAfterFailureInFirstChunk() throws Exception {
|
||||
MockRestartableItemReader reader = new MockRestartableItemReader() {
|
||||
public String read() throws Exception {
|
||||
@@ -756,6 +790,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStepToCompletion() throws Exception {
|
||||
|
||||
RepeatTemplate template = new RepeatTemplate();
|
||||
@@ -777,6 +812,7 @@ public class StepHandlerStepTests extends TestCase {
|
||||
* causes step to fail.
|
||||
* @throws JobInterruptedException
|
||||
*/
|
||||
@Test
|
||||
public void testStepFailureInAfterStepCallback() throws JobInterruptedException {
|
||||
StepExecutionListener listener = new StepExecutionListenerSupport() {
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.batch.sample.common;
|
||||
package org.springframework.batch.core.step.handler;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -19,13 +19,13 @@ import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Tests for {@link SystemCommandTasklet}.
|
||||
* Tests for {@link SystemCommandStepHandler}.
|
||||
*/
|
||||
public class SystemCommandTaskletIntegrationTests {
|
||||
public class SystemCommandStepHandlerIntegrationTests {
|
||||
|
||||
private static final Log log = LogFactory.getLog(SystemCommandTaskletIntegrationTests.class);
|
||||
private static final Log log = LogFactory.getLog(SystemCommandStepHandlerIntegrationTests.class);
|
||||
|
||||
private SystemCommandTasklet tasklet = new SystemCommandTasklet();
|
||||
private SystemCommandStepHandler tasklet = new SystemCommandStepHandler();
|
||||
|
||||
private StepExecution stepExecution = new StepExecution("systemCommandStep", new JobExecution(new JobInstance(
|
||||
1L, new JobParameters(), "systemCommandJob")));
|
||||
@@ -53,7 +53,7 @@ public class SystemCommandTaskletIntegrationTests {
|
||||
tasklet.afterPropertiesSet();
|
||||
|
||||
log.info("Executing command: " + command);
|
||||
ExitStatus exitStatus = tasklet.execute();
|
||||
ExitStatus exitStatus = tasklet.handle(null,null);
|
||||
|
||||
assertEquals(ExitStatus.FINISHED, exitStatus);
|
||||
}
|
||||
@@ -68,7 +68,7 @@ public class SystemCommandTaskletIntegrationTests {
|
||||
tasklet.afterPropertiesSet();
|
||||
|
||||
log.info("Executing command: " + command);
|
||||
ExitStatus exitStatus = tasklet.execute();
|
||||
ExitStatus exitStatus = tasklet.handle(null,null);
|
||||
|
||||
assertEquals(ExitStatus.FAILED, exitStatus);
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public class SystemCommandTaskletIntegrationTests {
|
||||
|
||||
log.info("Executing command: " + command);
|
||||
try {
|
||||
tasklet.execute();
|
||||
tasklet.handle(null,null);
|
||||
fail();
|
||||
}
|
||||
catch (SystemCommandException e) {
|
||||
@@ -105,7 +105,7 @@ public class SystemCommandTaskletIntegrationTests {
|
||||
|
||||
stepExecution.setTerminateOnly();
|
||||
try {
|
||||
tasklet.execute();
|
||||
tasklet.handle(null,null);
|
||||
fail();
|
||||
}
|
||||
catch (JobInterruptedException e) {
|
||||
@@ -1,260 +0,0 @@
|
||||
package org.springframework.batch.core.step.tasklet;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobInstance;
|
||||
import org.springframework.batch.core.JobInterruptedException;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.UnexpectedJobExecutionException;
|
||||
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
|
||||
import org.springframework.batch.core.step.JobRepositorySupport;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
|
||||
public class TaskletStepTests extends TestCase {
|
||||
|
||||
private StepExecution stepExecution;
|
||||
|
||||
private List<Serializable> list = new ArrayList<Serializable>();
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
stepExecution = new StepExecution("stepName", new JobExecution(new JobInstance(new Long(0L),
|
||||
new JobParameters(), "testJob"), new Long(12)));
|
||||
}
|
||||
|
||||
public void testTaskletMandatory() throws Exception {
|
||||
TaskletStep step = new TaskletStep();
|
||||
step.setJobRepository(new JobRepositorySupport());
|
||||
try {
|
||||
step.afterPropertiesSet();
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Message should contain 'tasklet': " + message, contains(message.toLowerCase(), "tasklet"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRepositoryMandatory() throws Exception {
|
||||
TaskletStep step = new TaskletStep();
|
||||
try {
|
||||
step.afterPropertiesSet();
|
||||
fail();
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Message should contain 'mandatory': " + message, contains(message.toLowerCase(), "mandatory"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testSuccessfulExecution() throws Exception {
|
||||
TaskletStep step = new TaskletStep(new StubTasklet(false, false), new JobRepositorySupport());
|
||||
step.execute(stepExecution);
|
||||
assertNotNull(stepExecution.getStartTime());
|
||||
assertEquals(ExitStatus.FINISHED, stepExecution.getExitStatus());
|
||||
assertNotNull(stepExecution.getEndTime());
|
||||
}
|
||||
|
||||
public void testSuccessfulExecutionWithStepContext() throws Exception {
|
||||
TaskletStep step = new TaskletStep(new StubTasklet(false, false, true), new JobRepositorySupport());
|
||||
step.afterPropertiesSet();
|
||||
step.execute(stepExecution);
|
||||
assertNotNull(stepExecution.getStartTime());
|
||||
assertEquals(ExitStatus.FINISHED, stepExecution.getExitStatus());
|
||||
assertNotNull(stepExecution.getEndTime());
|
||||
}
|
||||
|
||||
public void testSuccessfulExecutionWithExecutionContext() throws Exception {
|
||||
TaskletStep step = new TaskletStep(new StubTasklet(false, false), new JobRepositorySupport() {
|
||||
public void updateExecutionContext(StepExecution stepExecution) {
|
||||
list.add(stepExecution);
|
||||
}
|
||||
});
|
||||
step.execute(stepExecution);
|
||||
assertEquals(1, list.size());
|
||||
}
|
||||
|
||||
public void testSuccessfulExecutionWithFailureOnSaveOfExecutionContext() throws Exception {
|
||||
TaskletStep step = new TaskletStep(new StubTasklet(false, false, true), new JobRepositorySupport() {
|
||||
public void updateExecutionContext(StepExecution stepExecution) {
|
||||
throw new RuntimeException("foo");
|
||||
}
|
||||
});
|
||||
step.afterPropertiesSet();
|
||||
try {
|
||||
step.execute(stepExecution);
|
||||
fail("Expected BatchCriticalException");
|
||||
}
|
||||
catch (UnexpectedJobExecutionException e) {
|
||||
assertEquals("foo", e.getCause().getMessage());
|
||||
}
|
||||
assertEquals(BatchStatus.UNKNOWN, stepExecution.getStatus());
|
||||
}
|
||||
|
||||
public void testFailureExecution() throws Exception {
|
||||
TaskletStep step = new TaskletStep(new StubTasklet(true, false), new JobRepositorySupport());
|
||||
step.execute(stepExecution);
|
||||
assertNotNull(stepExecution.getStartTime());
|
||||
assertEquals(ExitStatus.FAILED, stepExecution.getExitStatus());
|
||||
assertNotNull(stepExecution.getEndTime());
|
||||
}
|
||||
|
||||
public void testSuccessfulExecutionWithListener() throws Exception {
|
||||
TaskletStep step = new TaskletStep(new StubTasklet(false, false), new JobRepositorySupport());
|
||||
step.setStepExecutionListeners(new StepExecutionListener[] { new StepExecutionListenerSupport() {
|
||||
public void beforeStep(StepExecution context) {
|
||||
list.add("open");
|
||||
}
|
||||
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
list.add("close");
|
||||
return ExitStatus.CONTINUABLE;
|
||||
}
|
||||
} });
|
||||
step.execute(stepExecution);
|
||||
assertEquals(2, list.size());
|
||||
}
|
||||
|
||||
public void testExceptionExecution() throws JobInterruptedException, UnexpectedJobExecutionException {
|
||||
TaskletStep step = new TaskletStep(new StubTasklet(false, true), new JobRepositorySupport());
|
||||
try {
|
||||
step.execute(stepExecution);
|
||||
fail();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
assertNotNull(stepExecution.getStartTime());
|
||||
assertEquals(ExitStatus.FAILED.getExitCode(), stepExecution.getExitStatus().getExitCode());
|
||||
assertNotNull(stepExecution.getEndTime());
|
||||
}
|
||||
}
|
||||
|
||||
public void testExceptionError() throws JobInterruptedException, UnexpectedJobExecutionException {
|
||||
TaskletStep step = new TaskletStep(new StubTasklet(new Error("Foo!")), new JobRepositorySupport());
|
||||
try {
|
||||
step.execute(stepExecution);
|
||||
fail();
|
||||
}
|
||||
catch (Error e) {
|
||||
assertNotNull(stepExecution.getStartTime());
|
||||
assertEquals(ExitStatus.FAILED.getExitCode(), stepExecution.getExitStatus().getExitCode());
|
||||
assertNotNull(stepExecution.getEndTime());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When job is interrupted the {@link JobInterruptedException} should be
|
||||
* propagated up.
|
||||
*/
|
||||
public void testJobInterrupted() throws Exception {
|
||||
TaskletStep step = new TaskletStep(new Tasklet() {
|
||||
public ExitStatus execute() throws Exception {
|
||||
throw new JobInterruptedException("Job interrupted while executing tasklet");
|
||||
}
|
||||
}, new JobRepositorySupport());
|
||||
|
||||
try {
|
||||
step.execute(stepExecution);
|
||||
fail();
|
||||
}
|
||||
catch (JobInterruptedException expected) {
|
||||
assertEquals("Job interrupted while executing tasklet", expected.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception in {@link StepExecutionListener#afterStep(StepExecution)}
|
||||
* causes step to fail.
|
||||
* @throws JobInterruptedException
|
||||
*/
|
||||
public void testStepFailureInAfterStepCallback() throws JobInterruptedException {
|
||||
TaskletStep step = new TaskletStep(new Tasklet() {
|
||||
public ExitStatus execute() throws Exception {
|
||||
return ExitStatus.FINISHED;
|
||||
}
|
||||
}, new JobRepositorySupport());
|
||||
|
||||
StepExecutionListener listener = new StepExecutionListenerSupport() {
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
throw new RuntimeException("exception thrown in afterStep to signal failure");
|
||||
}
|
||||
};
|
||||
step.setStepExecutionListeners(new StepExecutionListener[] { listener });
|
||||
try {
|
||||
step.execute(stepExecution);
|
||||
fail();
|
||||
}
|
||||
catch (RuntimeException expected) {
|
||||
assertEquals("exception thrown in afterStep to signal failure", expected.getMessage());
|
||||
}
|
||||
|
||||
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
|
||||
|
||||
}
|
||||
|
||||
private static class StubTasklet extends StepExecutionListenerSupport implements Tasklet {
|
||||
|
||||
private final boolean exitFailure;
|
||||
|
||||
private final boolean throwException;
|
||||
|
||||
private final boolean assertStepContext;
|
||||
|
||||
private StepExecution stepExecution;
|
||||
|
||||
private Throwable exception = null;
|
||||
|
||||
public StubTasklet(boolean exitFailure, boolean throwException) {
|
||||
this(exitFailure, throwException, false);
|
||||
}
|
||||
|
||||
public StubTasklet(boolean exitFailure, boolean throwException, boolean assertStepContext) {
|
||||
this.exitFailure = exitFailure;
|
||||
this.throwException = throwException;
|
||||
this.assertStepContext = assertStepContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param error
|
||||
*/
|
||||
public StubTasklet(Throwable error) {
|
||||
this(false, false, false);
|
||||
this.exception = error;
|
||||
}
|
||||
|
||||
public ExitStatus execute() throws Exception {
|
||||
if (throwException) {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
if (exception!=null) {
|
||||
if (exception instanceof Exception) throw (Exception) exception;
|
||||
if (exception instanceof Error) throw (Error) exception;
|
||||
}
|
||||
|
||||
if (exitFailure) {
|
||||
return ExitStatus.FAILED;
|
||||
}
|
||||
|
||||
if (assertStepContext) {
|
||||
assertNotNull(this.stepExecution);
|
||||
}
|
||||
|
||||
return ExitStatus.FINISHED;
|
||||
}
|
||||
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
this.stepExecution = stepExecution;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean contains(String str, String searchStr) {
|
||||
return str.indexOf(searchStr) != -1;
|
||||
}
|
||||
}
|
||||
@@ -15,19 +15,22 @@
|
||||
*/
|
||||
package org.springframework.batch.integration.job;
|
||||
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.step.handler.StepHandler;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class TestTasklet implements Tasklet {
|
||||
public class TestStepHandler implements StepHandler {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.core.step.tasklet.Tasklet#execute()
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*/
|
||||
public ExitStatus execute() throws Exception {
|
||||
public ExitStatus handle(StepContribution contribution, AttributeAccessor attributes) throws Exception {
|
||||
return ExitStatus.FINISHED;
|
||||
}
|
||||
|
||||
@@ -33,11 +33,10 @@
|
||||
<property name="steps" ref="step" />
|
||||
</bean>
|
||||
|
||||
<bean id="step" class="org.springframework.batch.core.step.tasklet.TaskletStep">
|
||||
<property name="tasklet">
|
||||
<bean class="org.springframework.batch.integration.job.TestTasklet" />
|
||||
<bean id="step" parent="handlerStep">
|
||||
<property name="stepHandler">
|
||||
<bean class="org.springframework.batch.integration.job.TestStepHandler" />
|
||||
</property>
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -26,9 +26,10 @@
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
<property name="restartable" value="true" />
|
||||
</bean>
|
||||
<bean id="taskletStep"
|
||||
class="org.springframework.batch.core.step.tasklet.TaskletStep"
|
||||
<bean id="handlerStep"
|
||||
class="org.springframework.batch.core.step.handler.StepHandlerStep"
|
||||
abstract="true">
|
||||
<property name="transactionManager" ref="transactionManager" />
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
<property name="allowStartIfComplete" value="true" />
|
||||
</bean>
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
<config>src/main/resources/jobs/skipSampleJob.xml</config>
|
||||
<config>src/main/resources/jobs/multilineOrderInputTokenizers.xml</config>
|
||||
<config>src/main/resources/jobs/multilineOrderOutputAggregators.xml</config>
|
||||
<config>src/main/resources/jobs/taskletJob.xml</config>
|
||||
<config>src/main/resources/jobs/compositeItemWriterSampleJob.xml</config>
|
||||
<config>src/main/resources/jobs/multiResourceJob.xml</config>
|
||||
<config>src/main/resources/jobs/jobExecutionContextSample.xml</config>
|
||||
@@ -261,7 +260,6 @@
|
||||
<configs>
|
||||
<config>src/main/resources/data-source-context.xml</config>
|
||||
<config>src/main/resources/data-source-context-init.xml</config>
|
||||
<config>src/main/resources/jobs/taskletJob.xml</config>
|
||||
<config>src/main/resources/simple-job-launcher-context.xml</config>
|
||||
<config>src/main/resources/org/springframework/batch/sample/config/common-context.xml</config>
|
||||
</configs>
|
||||
|
||||
@@ -2,28 +2,30 @@ package org.springframework.batch.sample.tasklet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.core.step.handler.StepHandler;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
|
||||
/**
|
||||
* Dummy tasklet that retrieves message from the job execution context.
|
||||
*/
|
||||
public class DummyMessageReceivingTasklet extends StepExecutionListenerSupport implements Tasklet {
|
||||
public class DummyMessageReceivingStepHandler extends StepExecutionListenerSupport implements StepHandler {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DummyMessageReceivingTasklet.class);
|
||||
private static final Log logger = LogFactory.getLog(DummyMessageReceivingStepHandler.class);
|
||||
|
||||
private String receivedMessage = null;
|
||||
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
ExecutionContext ctx = stepExecution.getJobExecution().getExecutionContext();
|
||||
receivedMessage = ctx.getString(DummyMessageSendingTasklet.MESSAGE_KEY);
|
||||
receivedMessage = ctx.getString(DummyMessageSendingStepHandler.MESSAGE_KEY);
|
||||
logger.info("Got message from context: " + receivedMessage);
|
||||
}
|
||||
|
||||
public ExitStatus execute() throws Exception {
|
||||
public ExitStatus handle(StepContribution contribution, AttributeAccessor attributes) throws Exception {
|
||||
return ExitStatus.FINISHED;
|
||||
}
|
||||
|
||||
@@ -2,20 +2,22 @@ package org.springframework.batch.sample.tasklet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.core.step.handler.StepHandler;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
|
||||
/**
|
||||
* Dummy tasklet that stores a message in the job execution context.
|
||||
*/
|
||||
public class DummyMessageSendingTasklet extends StepExecutionListenerSupport implements Tasklet {
|
||||
public class DummyMessageSendingStepHandler extends StepExecutionListenerSupport implements StepHandler {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DummyMessageSendingTasklet.class);
|
||||
private static final Log logger = LogFactory.getLog(DummyMessageSendingStepHandler.class);
|
||||
|
||||
public static final String MESSAGE_KEY = "DummyMessageSendingTasklet.MESSAGE";
|
||||
public static final String MESSAGE_KEY = "DummyMessageSendingStepHandler.MESSAGE";
|
||||
|
||||
private String message = "Hello!";
|
||||
|
||||
@@ -25,8 +27,8 @@ public class DummyMessageSendingTasklet extends StepExecutionListenerSupport imp
|
||||
logger.info("Put message into context: " + message);
|
||||
return null;
|
||||
}
|
||||
|
||||
public ExitStatus execute() throws Exception {
|
||||
|
||||
public ExitStatus handle(StepContribution contribution, AttributeAccessor attributes) throws Exception {
|
||||
return ExitStatus.FINISHED;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.springframework.batch.sample.tasklet;
|
||||
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.UnexpectedJobExecutionException;
|
||||
import org.springframework.batch.core.step.handler.StepHandler;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Deletes files from an array of resources, so a pattern can be used in
|
||||
* configuration, e.g. <code>resources="/home/batch/job/**"</code>
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class FileDeletingStepHandler implements StepHandler, InitializingBean {
|
||||
|
||||
private Resource[] resources;
|
||||
|
||||
public ExitStatus handle(StepContribution contribution, AttributeAccessor attributes) throws Exception {
|
||||
for (Resource resource : resources) {
|
||||
boolean deleted = resource.getFile().delete();
|
||||
if (!deleted) {
|
||||
throw new UnexpectedJobExecutionException("Could not delete file " + resource);
|
||||
}
|
||||
}
|
||||
return ExitStatus.FINISHED;
|
||||
}
|
||||
|
||||
public void setResources(Resource[] resources) {
|
||||
this.resources = resources;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(resources, "Resources must be set");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package org.springframework.batch.sample.tasklet;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.springframework.batch.core.UnexpectedJobExecutionException;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Deletes files in given directory. Ignores subdirectories. Fails (by throwing
|
||||
* exception) if any of the files could not be deleted.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class FileDeletingTasklet implements Tasklet, InitializingBean {
|
||||
|
||||
private Resource directory;
|
||||
|
||||
public ExitStatus execute() throws Exception {
|
||||
File dir = directory.getFile();
|
||||
Assert.state(dir.isDirectory());
|
||||
|
||||
File[] files = dir.listFiles();
|
||||
for (File file : files) {
|
||||
boolean deleted = file.delete();
|
||||
if (!deleted) {
|
||||
throw new UnexpectedJobExecutionException("Could not delete file " + file.getPath());
|
||||
}
|
||||
}
|
||||
return ExitStatus.FINISHED;
|
||||
}
|
||||
|
||||
public void setDirectoryResource(Resource directory) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(directory, "directory must be set");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
|
||||
|
||||
<description>
|
||||
Deletes files in given directory - TaskletStep is used as this
|
||||
is the kind of task that is not natural to split into read and
|
||||
@@ -17,23 +13,19 @@
|
||||
|
||||
The second step illustrates executing a system command.
|
||||
</description>
|
||||
|
||||
<bean id="taskletJob" parent="simpleJob">
|
||||
<bean id="handlerJob" parent="simpleJob">
|
||||
<property name="steps">
|
||||
<list>
|
||||
<bean id="deleteFilesInDir" parent="taskletStep">
|
||||
<bean id="deleteFilesInDir" parent="handlerStep">
|
||||
<property name="tasklet">
|
||||
<bean
|
||||
class="org.springframework.batch.sample.tasklet.FileDeletingTasklet">
|
||||
<property name="directoryResource"
|
||||
ref="directory" />
|
||||
<bean class="org.springframework.batch.sample.tasklet.FileDeletingStepHandler">
|
||||
<property name="resources" value="target/test-outputs/test-dir/*" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
<bean id="executeSystemCommand" parent="taskletStep">
|
||||
<bean id="executeSystemCommand" parent="handlerStep">
|
||||
<property name="tasklet">
|
||||
<bean
|
||||
class="org.springframework.batch.sample.common.SystemCommandTasklet">
|
||||
<bean class="org.springframework.batch.core.step.handler.SystemCommandStepHandler">
|
||||
<property name="command" value="java -version" />
|
||||
<!-- 5 second timeout for the command to complete -->
|
||||
<property name="timeout" value="5000" />
|
||||
@@ -42,12 +34,5 @@
|
||||
</bean>
|
||||
</list>
|
||||
</property>
|
||||
|
||||
</bean>
|
||||
|
||||
<bean id="directory"
|
||||
class="org.springframework.core.io.FileSystemResource">
|
||||
<constructor-arg value="target/test-outputs/test-dir" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -11,10 +11,10 @@
|
||||
<bean id="jobExecutionContextSample" parent="simpleJob">
|
||||
<property name="steps">
|
||||
<list>
|
||||
<bean id="step1" parent="taskletStep">
|
||||
<bean id="step1" parent="handlerStep">
|
||||
<property name="tasklet" ref="sender" />
|
||||
</bean>
|
||||
<bean id="step2" parent="taskletStep">
|
||||
<bean id="step2" parent="handlerStep">
|
||||
<property name="tasklet" ref="receiver" />
|
||||
</bean>
|
||||
</list>
|
||||
@@ -22,11 +22,11 @@
|
||||
</bean>
|
||||
|
||||
<bean id="sender"
|
||||
class="org.springframework.batch.sample.tasklet.DummyMessageSendingTasklet">
|
||||
class="org.springframework.batch.sample.tasklet.DummyMessageSendingStepHandler">
|
||||
<property name="message" value="Hey!" />
|
||||
</bean>
|
||||
|
||||
<bean id="receiver"
|
||||
class="org.springframework.batch.sample.tasklet.DummyMessageReceivingTasklet" />
|
||||
class="org.springframework.batch.sample.tasklet.DummyMessageReceivingStepHandler" />
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
<property name="restartable" value="true" />
|
||||
</bean>
|
||||
|
||||
<bean id="taskletStep" class="org.springframework.batch.core.step.tasklet.TaskletStep" abstract="true">
|
||||
<bean id="handlerStep" class="org.springframework.batch.core.step.handler.StepHandlerStep" abstract="true">
|
||||
<property name="transactionManager" ref="transactionManager" />
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
<property name="allowStartIfComplete" value="true" />
|
||||
</bean>
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.io.File;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -17,10 +17,9 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration()
|
||||
public class TaskletJobFunctionalTests extends AbstractValidatingBatchLauncherTests {
|
||||
public class HandlerJobFunctionalTests extends AbstractValidatingBatchLauncherTests {
|
||||
|
||||
@Autowired
|
||||
private Resource directory;
|
||||
private Resource directory = new FileSystemResource("target/test-outputs/test-dir");
|
||||
|
||||
/*
|
||||
* Create the directory and some files in it.
|
||||
@@ -3,8 +3,8 @@ package org.springframework.batch.sample;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.sample.tasklet.DummyMessageReceivingTasklet;
|
||||
import org.springframework.batch.sample.tasklet.DummyMessageSendingTasklet;
|
||||
import org.springframework.batch.sample.tasklet.DummyMessageReceivingStepHandler;
|
||||
import org.springframework.batch.sample.tasklet.DummyMessageSendingStepHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -14,10 +14,10 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
public class JobExecutionContextSampleFunctionalTests extends AbstractValidatingBatchLauncherTests {
|
||||
|
||||
@Autowired
|
||||
private DummyMessageSendingTasklet sender;
|
||||
private DummyMessageSendingStepHandler sender;
|
||||
|
||||
@Autowired
|
||||
private DummyMessageReceivingTasklet receiver;
|
||||
private DummyMessageReceivingStepHandler receiver;
|
||||
|
||||
protected void validatePostConditions() throws Exception {
|
||||
assertEquals(sender.getMessage(), receiver.getReceivedMessage());
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
|
||||
|
||||
<import resource="classpath:/simple-job-launcher-context.xml" />
|
||||
<import resource="classpath:/jobs/taskletJob.xml" />
|
||||
<import resource="classpath:/jobs/handlerJob.xml" />
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user