diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/IProcess.java b/common/source/java/ch/systemsx/cisd/common/utilities/IProcess.java new file mode 100644 index 0000000000000000000000000000000000000000..4f71c216741c5ba9e0b7a8fa627b57816fae807d --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/utilities/IProcess.java @@ -0,0 +1,52 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * 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 ch.systemsx.cisd.common.utilities; + +/** + * A <code>Runnable</code> extension that describes a process. + * + * @author Christian Ribeaud + */ +public interface IProcess extends Runnable +{ + + /** + * Whether this <code>IProcess</code> exited successfully. + * <p> + * Is typically called after {@link Runnable#run()} has performed. + * </p> + * + * @return <code>true</code> if this <code>IProcess</code> succeeds, terminating so the whole running process. + */ + public boolean succeeded(); + + /** + * The number of times we should try if this <code>IProcess</code> failed. + * <p> + * This is a static method: it only gets called once during the initialization process. + * </p> + */ + public int getMaxRetryOnFailure(); + + /** + * The number of milliseconds we should wait before re-executing {@link Runnable#run()}. + * <p> + * This is a static method: it only gets called once during the initialization process. + * </p> + */ + public long getMillisToSleepOnFailure(); +} diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/ProcessRunner.java b/common/source/java/ch/systemsx/cisd/common/utilities/ProcessRunner.java new file mode 100644 index 0000000000000000000000000000000000000000..d93a6b7f53d3099f2ed9f18df0f32e8a2d37c77b --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/utilities/ProcessRunner.java @@ -0,0 +1,55 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * 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 ch.systemsx.cisd.common.utilities; + +import ch.systemsx.cisd.common.exceptions.CheckedExceptionTunnel; + +/** + * This class takes cares of running encapsulated {@link IProcess}. + * + * @author Christian Ribeaud + */ +public final class ProcessRunner +{ + + public ProcessRunner(final IProcess process) + { + runProcess(process); + } + + final static void runProcess(final IProcess process) + { + final int maxRetryOnFailure = process.getMaxRetryOnFailure(); + final long millisToSleepOnFailure = process.getMillisToSleepOnFailure(); + assert millisToSleepOnFailure > -1; + int counter = 0; + do + { + process.run(); + if (counter > 0 && millisToSleepOnFailure > 0) + { + try + { + Thread.sleep(millisToSleepOnFailure); + } catch (InterruptedException ex) + { + throw new CheckedExceptionTunnel(ex); + } + } + } while (++counter < maxRetryOnFailure && process.succeeded() == false); + } +} diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ProcessRunnerTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ProcessRunnerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8f6fb89a9348ed62450529119493db71a22162e2 --- /dev/null +++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ProcessRunnerTest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * 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 ch.systemsx.cisd.common.utilities; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * Test cases for the {@link ProcessRunner}. + * + * @author Christian Ribeaud + */ +public class ProcessRunnerTest +{ + + private Mockery context; + + private IProcess process; + + @BeforeMethod + public final void beforeMethod() + { + context = new Mockery(); + process = context.mock(IProcess.class); + } + + @AfterMethod + public final void afterMethod() + { + // To following line of code should also be called at the end of each test method. + // Otherwise one do not known which test failed. + context.assertIsSatisfied(); + } + + @Test + public final void testRunSuccessfulProcess() + { + context.checking(new Expectations() + { + { + one(process).run(); + + one(process).succeeded(); + will(returnValue(true)); + + one(process).getMillisToSleepOnFailure(); + will(returnValue(1L)); + + one(process).getMaxRetryOnFailure(); + will(returnValue(2)); + } + }); + new ProcessRunner(process); + context.assertIsSatisfied(); + } + + @Test + public final void testRunUnsuccessfulProcess() + { + final int tries = 3; + context.checking(new Expectations() + { + { + exactly(tries).of(process).run(); + + exactly(tries - 1).of(process).succeeded(); + will(returnValue(false)); + + one(process).getMillisToSleepOnFailure(); + will(returnValue(1L)); + + one(process).getMaxRetryOnFailure(); + will(returnValue(tries)); + } + }); + new ProcessRunner(process); + context.assertIsSatisfied(); + } +}