From: <st...@us...> - 2006-10-16 16:54:53
|
Revision: 3481 http://svn.sourceforge.net/smartfrog/?rev=3481&view=rev Author: steve_l Date: 2006-10-16 09:54:24 -0700 (Mon, 16 Oct 2006) Log Message: ----------- Lots of reworking to the workflow bits. Not guaranteed to be working yet (i.e incomplete test coverage). What new tests there are, work, except for some sequence stuff. Modified Paths: -------------- trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestCompound.java trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestCompoundImpl.java trunk/core/smartfrog/src/org/smartfrog/services/assertions/components.sf trunk/core/smartfrog/src/org/smartfrog/services/assertions/testcomponents.sf trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Container.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Delay.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/DetachingCompoundImpl.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/During.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/FireBreak.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Parallel.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/RandomSequence.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Repeat.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Retry.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Run.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Sequence.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Timeout.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Try.java trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/during.sf trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/eventbus/EventCompoundImpl.java Added Paths: ----------- trunk/core/smartfrog/.svnignore trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestBlock.java trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestBlockImpl.java trunk/core/smartfrog/src/org/smartfrog/services/assertions/testblock.sf trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/DelayedTerminator.java Removed Paths: ------------- trunk/core/smartfrog/.cvsignore trunk/core/smartfrog/src/org/smartfrog/services/assertions/DelayedTerminator.java Deleted: trunk/core/smartfrog/.cvsignore =================================================================== --- trunk/core/smartfrog/.cvsignore 2006-10-16 16:53:00 UTC (rev 3480) +++ trunk/core/smartfrog/.cvsignore 2006-10-16 16:54:24 UTC (rev 3481) @@ -1,13 +0,0 @@ -build -dist -build.properties -runtime.properties -common.properties -*.jpx -*.ipr -*.iml -*.iws -.classpath -.project -parsertargets_report.html -smartfrog-version.properties Copied: trunk/core/smartfrog/.svnignore (from rev 3465, trunk/core/smartfrog/.cvsignore) =================================================================== --- trunk/core/smartfrog/.svnignore (rev 0) +++ trunk/core/smartfrog/.svnignore 2006-10-16 16:54:24 UTC (rev 3481) @@ -0,0 +1,13 @@ +build +dist +build.properties +runtime.properties +common.properties +*.jpx +*.ipr +*.iml +*.iws +.classpath +.project +parsertargets_report.html +smartfrog-version.properties Property changes on: trunk/core/smartfrog/.svnignore ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:eol-style + native Deleted: trunk/core/smartfrog/src/org/smartfrog/services/assertions/DelayedTerminator.java =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/services/assertions/DelayedTerminator.java 2006-10-16 16:53:00 UTC (rev 3480) +++ trunk/core/smartfrog/src/org/smartfrog/services/assertions/DelayedTerminator.java 2006-10-16 16:54:24 UTC (rev 3481) @@ -1,175 +0,0 @@ -/** (C) Copyright 2006 Hewlett-Packard Development Company, LP - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - For more information: www.smartfrog.org - - */ -package org.smartfrog.services.assertions; - -import org.smartfrog.sfcore.logging.LogSF; -import org.smartfrog.sfcore.prim.Prim; -import org.smartfrog.sfcore.prim.TerminationRecord; -import org.smartfrog.sfcore.reference.Reference; -import org.smartfrog.sfcore.utils.ComponentHelper; - -import java.lang.ref.WeakReference; -import java.rmi.RemoteException; - -/** - * Component to shut down a task after a delay. - * We retain a (weak) reference to a prim. The component can go off teh graph - * and we can still find it, but we dont preclude distributed GC taking place and killing the reference. - */ -public class DelayedTerminator implements Runnable { - private long time; - private WeakReference/*<Prim>*/ primref; - private Throwable terminationFault; - private Thread self; - private boolean shutdown; - private boolean shouldTerminate = true; - private LogSF log; - private String description; - private boolean normalTermination; - private boolean forcedShutdown; - - - /** - * Create a delayed time. - * If the time is -1, then the wait is for {@link Long.MAX_VALUE}, otherwise it - * is for as many milliseconds as needed. - * @param prim - * @param time - * @param log - * @param description - * @param normalTermination - */ - public DelayedTerminator(Prim prim, long time, LogSF log, String description, boolean normalTermination) { - if(time<0) { - time=Long.MAX_VALUE; - } - this.time = time; - this.primref = new WeakReference/*<Prim>*/(prim); - this.log = log; - if (description == null) { - this.description = "Terminate " - + new ComponentHelper(prim).completeNameSafe().toString() - + " after " + time + " milliseconds"; - } else { - this.description = description; - } - this.normalTermination = normalTermination; - } - - public Throwable getTerminationFault() { - return terminationFault; - } - - public void setTerminationFault(Throwable terminationFault) { - this.terminationFault = terminationFault; - } - - - /** - * Call from the owner to shut down the target. - * @param shouldTerminate - * @return - */ - public synchronized void shutdown(boolean shouldTerminate) throws RemoteException { - if (self != null) { - shutdown = true; - shouldTerminate = true; - self.interrupt(); - } - } - - - /** - * Start the new thread - */ - public synchronized void start() { - if(self!=null) { - if (self != null) { - self = new Thread(this); - self.start(); - } - } - } - - /** - * this is the thread that runs in the background - */ - public void run() { - - try { - Thread.sleep(time); - } catch (InterruptedException e) { - //we have been interrupted here. - log.debug("Interrupted " + description); - } - - if (shutdown == true) { - - //initiated shutdown - log.debug("initiated shutdown " + description); - } - - if (shouldTerminate) { - //termination time - Prim target = getTarget(); - if (target == null) { - log.debug("Target no longer exists for " + description); - } else { - try { - if (target.sfIsStarted()) { - forcedShutdown = true; - TerminationRecord record = createTerminationRecord(target); - target.sfTerminate(record); - } - } catch (RemoteException e) { - terminationFault = e; - } - } - } - - synchronized (this) { - //cease to exist - self = null; - primref = null; - } - - } - - private TerminationRecord createTerminationRecord(Prim target) { - Reference ref = new ComponentHelper(target).completeNameSafe(); - TerminationRecord record = new TerminationRecord( - normalTermination ? TerminationRecord.NORMAL : TerminationRecord.ABNORMAL, - "termination by delayed terminator", - ref); - return record; - } - - private Prim getTarget() { - return (Prim) primref.get(); - } - - /** - * Flag set to true if we forced system shutdown - * @return - */ - public synchronized boolean isForcedShutdown() { - return forcedShutdown; - } -} Added: trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestBlock.java =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestBlock.java (rev 0) +++ trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestBlock.java 2006-10-16 16:54:24 UTC (rev 3481) @@ -0,0 +1,113 @@ +/** (C) Copyright 2006 Hewlett-Packard Development Company, LP + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For more information: www.smartfrog.org + + */ +package org.smartfrog.services.assertions; + +import org.smartfrog.sfcore.prim.TerminationRecord; +import org.smartfrog.sfcore.prim.Prim; +import org.smartfrog.sfcore.common.SmartFrogException; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * created 13-Oct-2006 16:41:10 + */ + + +public interface TestBlock extends Remote { + + + /** + * {@value} + */ + String ATTR_FINISHED = "finished"; + + /** + * {@value} + */ + String ATTR_STATUS = "status"; + /** + * {@value} + */ + String ATTR_FAILED = "failed"; + /** + * {@value} + */ + String ATTR_SUCCEEDED = "succeeded"; + + + /** + * {@value} + */ + String ATTR_TIMEOUT = "timeout"; + + /** + * {@value} + */ + String ATTR_FORCEDTIMEOUT = "forcedTimeout"; + /** + * Name that a deployed action goes by + * {@value} + */ + String ACTION = "_action"; + + /** + * Return true iff the component is finished. + * Spin on this, with a (delay) between calls + * + * @return true if the test has finished + * @throws RemoteException on network trouble + * @throws SmartFrogException on other problems + */ + boolean isFinished() throws RemoteException, SmartFrogException; + + /** + * @return true only if the test has finished and failed + * @throws RemoteException on network trouble + * @throws SmartFrogException on other problems + */ + boolean isFailed() throws RemoteException, SmartFrogException; + + /** + * @return true iff the test succeeded + * @throws RemoteException on network trouble + * @throws SmartFrogException on other problems + */ + + boolean isSucceeded() throws RemoteException, SmartFrogException; + + /** + * Get the exit record + * + * @return the exit record, will be null for an unfinished child + * @throws RemoteException on network trouble + * @throws SmartFrogException on other problems + */ + TerminationRecord getStatus() throws RemoteException, SmartFrogException; + + + /** + * return the current action + * @return the child component. this will be null after termination. + * @throws RemoteException on network trouble + * @throws SmartFrogException on other problems + */ + Prim getAction() throws RemoteException, SmartFrogException; +} Added: trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestBlockImpl.java =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestBlockImpl.java (rev 0) +++ trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestBlockImpl.java 2006-10-16 16:54:24 UTC (rev 3481) @@ -0,0 +1,195 @@ +/** (C) Copyright 2006 Hewlett-Packard Development Company, LP + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For more information: www.smartfrog.org + + */ +package org.smartfrog.services.assertions; + +import org.smartfrog.sfcore.prim.TerminationRecord; +import org.smartfrog.sfcore.prim.Prim; +import org.smartfrog.sfcore.workflow.eventbus.EventCompoundImpl; +import org.smartfrog.sfcore.workflow.combinators.DelayedTerminator; +import org.smartfrog.sfcore.common.SmartFrogException; +import org.smartfrog.sfcore.common.SmartFrogDeploymentException; +import org.smartfrog.sfcore.utils.ComponentHelper; + +import java.rmi.RemoteException; + +/** + * created 13-Oct-2006 16:46:44 + */ + +public class TestBlockImpl extends EventCompoundImpl implements TestBlock { + + + private volatile boolean finished=false; + private volatile boolean failed = false; + private volatile boolean succeeded = false; + private volatile boolean forcedTimeout = false; + private volatile TerminationRecord status; + private DelayedTerminator actionTerminator; + private volatile Prim child; + public static final String ERROR_STARTUP_FAILURE = "Failed to start up action"; + + public TestBlockImpl() throws RemoteException { + } + + /** + * Return true iff the component is finished. + * Spin on this, with a (delay) between calls + * + * @return + */ + public boolean isFinished() { + return finished; + } + + /** + * @return true only if the test has finished and failed + */ + public boolean isFailed() { + return failed; + } + + /** + * @return true iff the test succeeded + */ + + public boolean isSucceeded() { + return succeeded; + } + + /** + * Get the exit record + * + * @return the exit record, will be null for an unfinished child + */ + public TerminationRecord getStatus() { + return status; + } + + /** + * return the current action + * @return the child component. this will be null after termination. + */ + public Prim getAction() { + return child; + } + + + /** + * Registers components referenced in the SendTo sub-component registers + * itself with components referenced in the RegisterWith sub-component. + * + * @throws java.rmi.RemoteException In case of network/rmi error + * @throws org.smartfrog.sfcore.common.SmartFrogDeploymentException + * In case of any error while + * deploying the component + */ + public synchronized void sfDeploy() throws SmartFrogException, RemoteException { + super.sfDeploy(); + checkActionDefined(); + } + + + /** + * Starts the compound. This sends a synchronous sfStart to all managed + * components in the compound context. Any failure will cause the compound + * to terminate + * + * @throws org.smartfrog.sfcore.common.SmartFrogException + * failed to start compound + * @throws java.rmi.RemoteException In case of Remote/nework error + */ + public synchronized void sfStart() throws SmartFrogException, RemoteException { + super.sfStart(); + long timeout = sfResolve(ATTR_TIMEOUT,0L,true); + try { + child=sfCreateNewChild(ACTION,action, null); + if(timeout>0) { + actionTerminator=new DelayedTerminator(child, timeout, sfLog(),"timeout",false ); + } + } catch (RemoteException e) { + startupException(e); + } catch (SmartFrogDeploymentException e) { + startupException(e); + } + } + + /** + * turn a startup exception into a cleaner exit + * @param e exception + */ + private void startupException(Exception e) { + //this is called if we failed during startup + TerminationRecord fault = TerminationRecord.abnormal(ERROR_STARTUP_FAILURE, name, e); + end(fault); + } + + /** + * log the end of the event. This may trigger workflow termination. + * does nothing if finished==true. + * @param record + */ + private synchronized void end(TerminationRecord record) { + if(finished) { + //non-reentrant + return; + } + finished=true; + status = record; + succeeded=record.isNormal(); + failed=!succeeded; + if(actionTerminator!=null) { + //test to see if the terminator caused the shutdown + forcedTimeout = actionTerminator.isForcedShutdown(); + //and terminate it quietly too. + actionTerminator.shutdown(false); + actionTerminator = null; + } + //this can trigger a shutdown if we want it + new ComponentHelper(this).sfSelfDetachAndOrTerminate(record); + } + + + /** + * This is an override point; it is where subclasses get to change their workflow + * depending on what happens underneath. + * It is only called outside of component termination, i.e. when {@link #isWorkflowTerminating()} is + * false, and when the comp parameter is a child, that is <code>sfContainsChild(comp)</code> holds. + * If the the method returns true, the event is forwarded up the object heirarchy, which + * will eventually trigger a component termination. + * <p/> + * Always return false if you start new components from this method! + * </p> + * + * @param status exit record of the component + * @param comp child component that is terminating + * @return true if the termination event is to be forwarded up the chain. + */ + protected boolean onChildTerminated(TerminationRecord status, Prim comp) { + if(comp==child) { + //this is the action terminating, so log the closure and continue + end(status); + return false; + } else { + //something unknown + return true; + } + } + +} Modified: trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestCompound.java =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestCompound.java 2006-10-16 16:53:00 UTC (rev 3480) +++ trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestCompound.java 2006-10-16 16:54:24 UTC (rev 3481) @@ -63,6 +63,13 @@ String ATTR_TESTS = "tests"; /** + * Time in milliseconds for the tests to successfully finish, use -1 for no limit. + * If this is set, then the tests are deemed to have failed if they have not finished + * by the time the tests are forcibly terminated + */ + String ATTR_TEST_TIMEOUT = "testTimeout"; + + /** * a component that is run after the tests pass/fail to perform teardown or post-process. the action will always have been terminated (cleanly or not) Modified: trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestCompoundImpl.java =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestCompoundImpl.java 2006-10-16 16:53:00 UTC (rev 3480) +++ trunk/core/smartfrog/src/org/smartfrog/services/assertions/TestCompoundImpl.java 2006-10-16 16:54:24 UTC (rev 3481) @@ -25,8 +25,8 @@ import org.smartfrog.sfcore.logging.LogSF; import org.smartfrog.sfcore.prim.Prim; import org.smartfrog.sfcore.prim.TerminationRecord; -import org.smartfrog.sfcore.prim.PrimImpl; import org.smartfrog.sfcore.workflow.eventbus.EventCompoundImpl; +import org.smartfrog.sfcore.workflow.combinators.DelayedTerminator; import org.smartfrog.sfcore.utils.ComponentHelper; import org.smartfrog.sfcore.componentdescription.ComponentDescription; @@ -37,22 +37,23 @@ */ public class TestCompoundImpl extends EventCompoundImpl implements TestCompound { - protected ComponentDescription teardownCD; - protected Prim teardown; + private ComponentDescription teardownCD; + private Prim teardown; - protected ComponentDescription testsCD; - protected Prim tests; + private ComponentDescription tests; + private Prim testsPrim; protected static final String ACTION_RUNNING = "_actionRunning"; - protected long undeployAfter; - protected boolean expectTerminate; - protected DelayedTerminator actionTerminator; - protected DelayedTerminator testsTerminator; - protected Prim actionPrim; - protected boolean terminating=false; - protected String exitType; + protected static final String TESTS_RUNNING = "_testsRunning"; + private long undeployAfter; + private long testTimeout; + private boolean expectTerminate; + private DelayedTerminator actionTerminator; + private DelayedTerminator testsTerminator; + private Prim actionPrim; + private String exitType; - protected String exitText; + private String exitText; /** * The message of a forced shutdown. This is important, as we look for * it when the component is terminated, and can use its presence to infer @@ -78,15 +79,13 @@ super.sfDeploy(); checkActionDefined(); name = sfCompleteNameSafe(); - testsCD = sfResolve(ATTR_TESTS, testsCD, false); - if (testsCD != null) { - throw new SmartFrogException("Not yet supported " + ATTR_TESTS); - } + tests = sfResolve(ATTR_TESTS, tests, false); + testTimeout = sfResolve(ATTR_TEST_TIMEOUT, 0L, true); teardownCD = sfResolve(ATTR_TEARDOWN, teardownCD, false); if (teardownCD != null) { throw new SmartFrogException("Not yet supported " + ATTR_TEARDOWN); } - undeployAfter = sfResolve(ATTR_UNDEPLOY_AFTER, 0,true); + undeployAfter = sfResolve(ATTR_UNDEPLOY_AFTER, 0L,true); expectTerminate = sfResolve(ATTR_EXPECT_TERMINATE,false,true); exitType = sfResolve(ATTR_EXIT_TYPE,exitType,true); exitText = sfResolve(ATTR_EXIT_TEXT, exitText, true); @@ -134,9 +133,15 @@ } //now deploy the tests. - //these are expected to pass + //any failure in tests is something to report, as is any failure of the tests to finish. - + if(tests !=null) { + testsPrim = sfCreateNewChild(TESTS_RUNNING, tests, null); + //the test terminator reports a termination as a failure + testsTerminator = new DelayedTerminator(testsPrim, testTimeout, logSF, + FORCED_TERMINATION, + false); + } } /** @@ -162,37 +167,51 @@ if(actionPrim!=null) { sfPingChildAndTerminateOnFailure(actionPrim); } - if (tests != null) { - sfPingChildAndTerminateOnFailure(tests); + if (testsPrim != null) { + sfPingChildAndTerminateOnFailure(testsPrim); } } public synchronized void sfTerminateWith(TerminationRecord status) { - terminating=true; super.sfTerminateWith(status); - if (actionTerminator!=null) { - //shut down the action terminator if we need to. We assume the superclass - //terminator is already terminating the children, so there is no need - //to tell the action terminator to do it. - try { - actionTerminator.shutdown(false); - } catch (RemoteException e) { - sfLog().error("When shutting down the action terminator",e); - } + shutdown(actionTerminator); + shutdown(testsTerminator); + } + + /** + * shut down the action terminator if we need to. We assume the superclass + * terminator is already terminating the children, so there is no need + * to tell the action terminator to do it. Therefore, this should only be called + * from sfTerminateWith + * + * @param terminator + */ + private void shutdown(DelayedTerminator terminator) { + if (terminator !=null) { + terminator.shutdown(false); } } + /** - * Called when a child terminates, either normally or abnormally. - * @param status - * @param comp + * This is an override point; it is where subclasses get to change their workflow + * depending on what happens underneath. + * It is only called outside of component termination, i.e. when {@link #isWorkflowTerminating()} is + * false, and when the comp parameter is a child, that is <code>sfContainsChild(comp)</code> holds. + * If the the method returns true, the event is forwarded up the object heirarchy, which + * will eventually trigger a component termination. + * <p/> + * Always return false if you start new components from this method! + * </p> + * + * @param status exit record of the component + * @param comp child component that is terminating + * @return true if the termination event is to be forwarded up the chain. */ - public void sfTerminatedWith(TerminationRecord status, Prim comp) { - - if (terminating) { - super.sfTerminatedWith(status,comp); - return; - } + protected boolean onChildTerminated(TerminationRecord status, Prim comp) { + boolean forward=true; + boolean tearDownTime=false; + TerminationRecord error=null; if (actionPrim == comp) { if (actionTerminator.isForcedShutdown() && expectTerminate == false) { //this is a forced shutdown, all is well @@ -227,16 +246,46 @@ + "and error text " + exitText + "\n" + "but got " + status; sfLog().error(errorText); - status = TerminationRecord.abnormal(errorText, status.id); + error = TerminationRecord.abnormal(errorText, status.id); } } + tearDownTime=true; + } else if(comp == testsPrim) { + //tests are terminating. + //it is an error if these terminated abnormally, for any reason at all. + //that is: test failure triggers an undeployment. + //There is no need to check this, because its implicit. + if(!status.isNormal()) { + sfLog().info("Tests have failed"); + } + tearDownTime=true; } - //TODO: start teardown, etc. - //trigger workflow termination. - new ComponentHelper(this).sfSelfDetachAndOrTerminate(status); + //start teardown, etc. + //kicks in on normal abnormal ter + if(tearDownTime && teardownCD!=null) { + try { + sfCreateNewChild(name + "_teardownRunning", teardownCD, null); + forward = false; + } catch (Exception e) { + error = TerminationRecord.abnormal("failed to start teardown", + name,e); + } + } + + //if the error record is non null, terminate ourselves with the new record + if (error != null) { + sfTerminate(error); + //dont forward, as we are terminating with an error + forward = false; + } + //trigger termination. + return forward; } + + + protected Prim deployAction() throws RemoteException, SmartFrogDeploymentException { Prim child = sfCreateNewChild(ACTION_RUNNING, action, null); return child; Modified: trunk/core/smartfrog/src/org/smartfrog/services/assertions/components.sf =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/services/assertions/components.sf 2006-10-16 16:53:00 UTC (rev 3480) +++ trunk/core/smartfrog/src/org/smartfrog/services/assertions/components.sf 2006-10-16 16:54:24 UTC (rev 3481) @@ -30,6 +30,7 @@ #include "/org/smartfrog/components.sf" #include "/org/smartfrog/predicates.sf" #include "/org/smartfrog/services/assertions/testcomponents.sf" + #include "/org/smartfrog/services/assertions/testblock.sf" /** @@ -79,7 +80,7 @@ * the assert component can be used to declare invariants which * must hold for the system to be valid */ -Assert extends Prim { +Assert extends WorkflowPrim { assertSchema extends Schema; sfClass "org.smartfrog.services.assertions.AssertComponent"; isTrue true; @@ -90,8 +91,6 @@ } - - /** * This is a component that will fail on demand. * Its original role was for testing workflows such as repeat, retry, etc, Added: trunk/core/smartfrog/src/org/smartfrog/services/assertions/testblock.sf =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/services/assertions/testblock.sf (rev 0) +++ trunk/core/smartfrog/src/org/smartfrog/services/assertions/testblock.sf 2006-10-16 16:54:24 UTC (rev 3481) @@ -0,0 +1,56 @@ +/** (C) Copyright 2006 Hewlett-Packard Development Company, LP + +Disclaimer of Warranty + +The Software is provided "AS IS," without a warranty of any kind. ALL +EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE HEREBY +EXCLUDED. SmartFrog is not a Hewlett-Packard Product. The Software has +not undergone complete testing and may contain errors and defects. It +may not function properly and is subject to change or withdrawal at +any time. The user must assume the entire risk of using the +Software. No support or maintenance is provided with the Software by +Hewlett-Packard. Do not install the Software if you are not accustomed +to using experimental software. + +Limitation of Liability + +TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT WILL HEWLETT-PACKARD +OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR +FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, +HOWEVER CAUSED REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF +OR RELATED TO THE FURNISHING, PERFORMANCE, OR USE OF THE SOFTWARE, OR +THE INABILITY TO USE THE SOFTWARE, EVEN IF HEWLETT-PACKARD HAS BEEN +ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. FURTHERMORE, SINCE THE +SOFTWARE IS PROVIDED WITHOUT CHARGE, YOU AGREE THAT THERE HAS BEEN NO +BARGAIN MADE FOR ANY ASSUMPTIONS OF LIABILITY OR DAMAGES BY +HEWLETT-PACKARD FOR ANY REASON WHATSOEVER, RELATING TO THE SOFTWARE OR +ITS MEDIA, AND YOU HEREBY WAIVE ANY CLAIM IN THIS REGARD. + +*/ + +#include "/org/smartfrog/sfcore/workflow/eventbus/eventbus.sf" + +/** + * the testblock component is for ease of testing from JUnit tests which talk to this deployed + * component using RMI. + * It deploys its action and remembers whether or not the result was successful or not. + * success/failure results are stored as attributes. + * + * It does not terminate until asked, by default. + */ +TestBlock extends ActionCompound { + + sfClass "org.smartfrog.services.assertions.TestBlockImpl"; + //timeout in milliseconds + timeout 0; + + //finished: boolean + //status -> exit status + //failed: boolean + //succeeded: boolean + //forcedtimeout: boolean , true iff timeout was forced + + +} \ No newline at end of file Modified: trunk/core/smartfrog/src/org/smartfrog/services/assertions/testcomponents.sf =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/services/assertions/testcomponents.sf 2006-10-16 16:53:00 UTC (rev 3480) +++ trunk/core/smartfrog/src/org/smartfrog/services/assertions/testcomponents.sf 2006-10-16 16:54:24 UTC (rev 3481) @@ -31,22 +31,21 @@ */ + /** + * Include the core smartfrog components. + */ +#include "/org/smartfrog/sfcore/workflow/combinators/sequence.sf" + /* this file declares components that can act as test deployers. - 1. The tests are all workflow compoundsl they run a test, then assertions + This isthe */ - - /** - * Include the core smartfrog components. - */ -#include "/org/smartfrog/sfcore/workflow/combinators/sequence.sf" - TestCompound extends ActionCompound { TestCompoundSchema extends Schema { description extends String { @@ -81,7 +80,6 @@ description ## time in milliseconds to trigger an exception if the tests do not finish; use -1 for no limit" - NOT IMPLEMENTED #; } teardown extends OptionalCD { @@ -115,6 +113,8 @@ //default to one minute undeployAfter 60000; + //no delay by default + testTimeout -1; //default to one minute startupTimeout 60000; //default to one minute @@ -192,4 +192,4 @@ forcePing true; pingInterval 0; -} \ No newline at end of file +} Modified: trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Container.java =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Container.java 2006-10-16 16:53:00 UTC (rev 3480) +++ trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Container.java 2006-10-16 16:54:24 UTC (rev 3481) @@ -47,9 +47,9 @@ * @throws java.rmi.RemoteException In case of network or RMI failure. */ public Container() throws java.rmi.RemoteException { - super(); } + /** * It is invoked by sub-components at * termination. It simply removes the child and continues to run @@ -57,18 +57,29 @@ * @param status termination status of sender * @param comp sender of termination */ - public void sfTerminatedWith(TerminationRecord status, Prim comp) { - if (sfContainsChild(comp)) { - try { - sfRemoveChild(comp); - } catch (Exception e) { - if (sfLog().isErrorEnabled()) { - sfLog().error(this.sfCompleteNameSafe()+ " - error handling child termination ", e); - } + /** + * If normal termination, Parallel behaviour is to terminate + * that component but leave the others running if it is the last - + * terminate normally. if an erroneous termination - + * terminate immediately passing on the error + * + * + * @param status exit record of the component + * @param comp child component that is terminating + * @return true if the termination event is to be forwarded up the chain. + */ + protected boolean onChildTerminated(TerminationRecord status, Prim comp) { + try { + sfRemoveChild(comp); + } catch (Exception e) { + if (sfLog().isErrorEnabled()) { + sfLog().error(this.sfCompleteNameSafe() + " - error handling child termination ", e); } } + return false; } + /** * Handle ping failures. Default behavior is to terminate with a liveness * send failure record storing the name of the target of the ping when the failure Modified: trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Delay.java =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Delay.java 2006-10-16 16:53:00 UTC (rev 3480) +++ trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/Delay.java 2006-10-16 16:54:24 UTC (rev 3481) @@ -46,43 +46,34 @@ * may be passed to Delay. * </p> */ -public class Delay - extends EventCompoundImpl implements Compound { +public class Delay extends EventCompoundImpl implements Compound, Runnable { protected static final String ATTR_TIME = "time"; /** * Reference for attribute time */ static Reference timeRef = new Reference(ATTR_TIME); - /** - * ComponentDescription - */ - ComponentDescription action; /** * Time taken. */ - int time; - /** - * Name of the component. - */ - Reference name; + private int time; /** * Timer thread. */ - Thread timer; + private volatile Thread timer; /** * Indication that the component has been terminated before the time fires */ - private boolean terminated = false; + private volatile boolean terminated = false; /** * Constructs Delay object. * * @throws java.rmi.RemoteException In case of RMI or network failure. */ - public Delay() throws java.rmi.RemoteException { + public Delay() throws RemoteException { super(); } @@ -99,7 +90,6 @@ super.sfDeploy(); checkActionDefined(); time = ((Integer)sfResolve(timeRef)).intValue(); - name = sfCompleteNameSafe(); } /** @@ -112,50 +102,68 @@ public synchronized void sfStart() throws SmartFrogException, RemoteException { super.sfStart(); - timer = new Thread(new Runnable() { - public void run() { - if (time>0) { - try { - Thread.sleep(time); - } catch (Exception e) { - // ignore - } - if (!terminated) { - try { - synchronized (this) { - (Delay.this).sfCreateNewChild(name+"_actionRunning", action, null); - } - } catch (Exception e) { - (Delay.this).sfTerminate(TerminationRecord.abnormal( - "error in launching delayed component", null)); - } - } - } - } - }); + timer = new Thread(this); timer.start(); } + /** - * Terminates the component. It is invoked by sub-components at - * termination. If normal termination, Delay behaviour is to terminate - * normally, otherwise abnormally. + * If normal termination, Parallel behaviour is to terminate + * that component but leave the others running if it is the last - + * terminate normally. if an erroneous termination - + * terminate immediately passing on the error * - * @param status termination status of sender - * @param comp sender of termination + * + * @param status exit record of the component + * @param comp child component that is terminating + * @return true if the termination event is to be forwarded up the chain. */ - public synchronized void sfTerminatedWith(TerminationRecord status, - Prim comp) { - if (sfContainsChild(comp)) { - if (timer!=null) { + protected boolean onChildTerminated(TerminationRecord status, Prim comp) { + killTimer(); + return true; + } + + /** + * kill the timer. + */ + private void killTimer() { + //copy + Thread t; + //copy so that even if the timer nulls itself during termination, the cached reference is + //still held. + t = timer; + if (t!=null) { + terminated = true; + t.interrupt(); + } + } + + + /** + * This routine runs in the background thread; it sleeps until its time is up + */ + public void run() { + try { + if (time > 0) { try { - terminated = true; - timer.interrupt(); - } catch (Exception e) { - //ignore + Thread.sleep(time); + } catch (InterruptedException ignored) { + // ignore } } - sfTerminate(status); + synchronized (this) { + if (!terminated && !isWorkflowTerminating()) { + try { + sfCreateNewChild(name + "_actionRunning", action, null); + } catch (Exception e) { + sfTerminate(TerminationRecord.abnormal( + "error in launching delayed component", name, e)); + } + } + } + } finally { + //now, cease to exit. + timer = null; } } } Copied: trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/DelayedTerminator.java (from rev 3465, trunk/core/smartfrog/src/org/smartfrog/services/assertions/DelayedTerminator.java) =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/DelayedTerminator.java (rev 0) +++ trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/DelayedTerminator.java 2006-10-16 16:54:24 UTC (rev 3481) @@ -0,0 +1,197 @@ +/** (C) Copyright 2006 Hewlett-Packard Development Company, LP + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For more information: www.smartfrog.org + + */ +package org.smartfrog.sfcore.workflow.combinators; + +import org.smartfrog.sfcore.logging.LogSF; +import org.smartfrog.sfcore.prim.Prim; +import org.smartfrog.sfcore.prim.TerminationRecord; +import org.smartfrog.sfcore.reference.Reference; +import org.smartfrog.sfcore.utils.ComponentHelper; + +import java.lang.ref.WeakReference; +import java.rmi.RemoteException; + +/** + * Component to shut down a task after a delay. + * We retain a (weak) reference to a prim. The component can go off the graph + * and we can still find it, but we don't preclude distributed GC taking place and killing the reference. + * This stops accidental retention of a terminator thread from keeping the prim around. + */ +public class DelayedTerminator implements Runnable { + private long time; + private WeakReference/*<Prim>*/ primref; + private Throwable terminationFault; + private volatile Thread self; + private volatile boolean shutdown; + private volatile boolean shouldTerminate = true; + private LogSF log; + private String description; + private volatile boolean normalTermination; + private volatile boolean forcedShutdown; + private String name; + + + /** + * Create a delayed time. + * If the time is -1, then the wait is for {@link Long#MAX_VALUE}, otherwise it + * is for as many milliseconds as needed. + * @param prim + * @param time + * @param log + * @param description + * @param normalTermination + */ + public DelayedTerminator(Prim prim, long time, LogSF log, String description, boolean normalTermination) { + if(time<0) { + time=Long.MAX_VALUE; + } + this.time = time; + primref = new WeakReference/*<Prim>*/(prim); + this.log = log; + if (description == null) { + this.description = "Terminate " + + new ComponentHelper(prim).completeNameSafe().toString() + + " after " + time + " milliseconds"; + } else { + this.description = description; + } + this.normalTermination = normalTermination; + } + + public Throwable getTerminationFault() { + return terminationFault; + } + + public void setTerminationFault(Throwable terminationFault) { + this.terminationFault = terminationFault; + } + + + public boolean isNormalTermination() { + return normalTermination; + } + + public void setNormalTermination(boolean normalTermination) { + this.normalTermination = normalTermination; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * Call from the owner to shut down the target. + * @param terminateTarget should we terminate the child? + */ + public synchronized void shutdown(boolean terminateTarget) { + if (self != null) { + shutdown = true; + this.shouldTerminate = terminateTarget; + self.interrupt(); + } + } + + + /** + * Start the new thread + */ + public synchronized void start() { + if(self!=null) { + if (self != null) { + self = new Thread(this); + if(name!=null) { + self.setName(name); + } + self.start(); + } + } + } + + /** + * this is the thread that runs in the background + */ + public void run() { + + try { + Thread.sleep(time); + } catch (InterruptedException e) { + //we have been interrupted here. + log.debug("Interrupted " + description); + } + + synchronized (this) { + try { + if (shutdown == true) { + + //initiated shutdown + log.debug("initiated shutdown " + description); + } + + if (shouldTerminate) { + //termination time + Prim target = getTarget(); + if (target == null) { + log.debug("Target no longer exists for " + description); + } else { + try { + if (target.sfIsStarted()) { + forcedShutdown = true; + TerminationRecord record = createTerminationRecord(target); + target.sfTerminate(record); + } + } catch (RemoteException e) { + terminationFault = e; + } + } + } + } finally { + //cease to exist + self = null; + primref = null; + } + } + + } + + private TerminationRecord createTerminationRecord(Prim target) { + Reference ref = new ComponentHelper(target).completeNameSafe(); + TerminationRecord record = new TerminationRecord( + normalTermination ? TerminationRecord.NORMAL : TerminationRecord.ABNORMAL, + "termination by delayed terminator", + ref); + return record; + } + + private Prim getTarget() { + return (Prim) primref.get(); + } + + /** + * Flag set to true if we forced system shutdown + * @return whether system was forced + */ + public synchronized boolean isForcedShutdown() { + return forcedShutdown; + } +} Property changes on: trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/DelayedTerminator.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:eol-style + native Modified: trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/DetachingCompoundImpl.java =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/DetachingCompoundImpl.java 2006-10-16 16:53:00 UTC (rev 3480) +++ trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/DetachingCompoundImpl.java 2006-10-16 16:54:24 UTC (rev 3481) @@ -43,30 +43,20 @@ /** * Name of the component. */ - Reference name = null; + private Reference name = null; /** * Set to true if you want the compound to detach its children on start */ - boolean detachDownwards; + private boolean detachDownwards; /** * Set to true if you want the compound to detach itself on start */ - boolean detachUpwards; + private boolean detachUpwards; /** - * Set to true if you want the compound to terminate at the end of the + * Set to true if you want the compound to terminate at the end of the run */ - boolean autoDestruct; + private boolean autoDestruct; /** - * Flag to indicate terminate is called. - */ - boolean terminateCalled = false; - /** - * A thread for lifecycle operations during the start phase - */ - Thread detacher; - - // boolean normalDeath = true; - /** * Constructs DetachingCompoundImpl. * @throws RemoteException if there is any network or RMI error */ @@ -108,65 +98,70 @@ public synchronized void sfStart() throws SmartFrogException, RemoteException { try { super.sfStart(); - detacher = new Thread(new Runnable() { - public void run() { - // detach this compound - if (detachUpwards) { - try { - sfDetach(); - } catch (SmartFrogException dex) { - sfTerminate(TerminationRecord.abnormal( - "DetachingCompound failed to detach ", - null)); + Thread detacher = new Thread(new Detacher()); - return; - } catch (RemoteException rex) { - sfTerminate(TerminationRecord.abnormal( - "DetachingCompound failed to detach due to remote exception", - null)); + if (detachDownwards || detachUpwards || autoDestruct) { + detacher.start(); + } + } catch (Throwable t) { // catch throwable as user code is involved + throw SmartFrogLifecycleException.sfDeploy("",t , this); + } + } - return; - } - } + /** + * Non-static inner class to handle detachment + */ + private class Detacher implements Runnable { + public void run() { + // detach this compound + if (detachUpwards) { + try { + sfDetach(); + } catch (SmartFrogException dex) { + sfTerminate(TerminationRecord.abnormal( + "DetachingCompound failed to detach ", + null)); - if (detachDownwards) { - // detach all children - for (Enumeration e = sfChildren(); - e.hasMoreElements();) { - try { - ((Prim) e.nextElement()).sfDetach(); - } catch (SmartFrogException remex) { - sfTerminate(TerminationRecord.abnormal( - "DetachingCompound failed to detach children", - null)); + return; + } catch (RemoteException rex) { + sfTerminate(TerminationRecord.abnormal( + "DetachingCompound failed to detach due to remote exception", + null)); - return; - } catch (RemoteException rex) { - sfTerminate(TerminationRecord.abnormal( - "DetachingCompound failed to detach children due to remote exception", - null)); + return; + } + } - return; - } - } + if (detachDownwards) { + // detach all children + for (Enumeration e = sfChildren(); + e.hasMoreElements();) { + try { + ((Prim) e.nextElement()).sfDetach(); + } catch (SmartFrogException remex) { + sfTerminate(TerminationRecord.abnormal( + "DetachingCompound failed to detach children", + null)); - if (autoDestruct) { - try { - name = sfCompleteName(); - } catch (Exception e) { - } + return; + } catch (RemoteException rex) { + sfTerminate(TerminationRecord.abnormal( + "DetachingCompound failed to detach children due to remote exception", + null)); - sfTerminate(TerminationRecord.normal(name)); - } - } - } - }); + return; + } + } - if (detachDownwards || detachUpwards || autoDestruct) { - detacher.start(); + if (autoDestruct) { + try { + name = sfCompleteName(); + } catch (Exception e) { + } + + sfTerminate(TerminationRecord.normal(name)); + } } - } catch (Throwable t) { // catch throwable as user code is involved - throw SmartFrogLifecycleException.sfDeploy("",t , this); } } } Modified: trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/During.java =================================================================== --- trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/During.java 2006-10-16 16:53:00 UTC (rev 3480) +++ trunk/core/smartfrog/src/org/smartfrog/sfcore/workflow/combinators/During.java 2006-10-16 16:54:24 UTC (rev 3481) @@ -57,16 +57,13 @@ /** * Time taken. */ - int time; + private int time; + /** - * Timer thread. + * Terminator thread */ - Thread timer; + private DelayedTerminator terminator; - boolean abortTimer = false; - - String name = ""; - /** * Constructs During. * @@ -100,44 +97,77 @@ */ public synchronized void sfStart() throws SmartFrogException, RemoteException { super.sfStart(); - name = sfCompleteNameSafe().toString(); - Runnable terminator = new Runnable() { - public void run() { - if (sfLog().isDebugEnabled()) { sfLog().debug("Timer set:" +time+". Going to sleep "+name);} - if (time > 0) { - try { Thread.sleep(time); } catch (Exception ex) { if (abortTimer) return; } - String terminationMessage = "Timer '"+time+"' expired. Terminating "+name; - if (sfLog().isDebugEnabled()) { sfLog().debug(terminationMessage);} - sfTerminate(new TerminationRecord(TerminationRecord.NORMAL, terminationMessage , null)); - } - } - }; - timer = new Thread(terminator); - timer.setName(name+"_DuringTerminator"); - timer.start(); - + terminator=new DelayedTerminator(this,time,sfLog(), name + "_DuringTerminator",true); + terminator.start(); sfCreateNewChild(name+"_duringActionRunning", action, null); } + /** - * Terminates the component. This is invoked by sub-components on - * termination. If normal termiantion, Timeout behaviour is to terminate - * normally, otherwise abnormally. + * Deregisters from all current registrations. * - * @param status termination status of sender - * @param comp sender of termination + * @param status Termination Record */ - public void sfTerminatedWith(TerminationRecord status, Prim comp) { - if (sfContainsChild(comp)) { - if (timer != null) { + public synchronized void sfTerminateWith(TerminationRecord status) { + super.sfTerminateWith(status); + killTimer(); + } + + /** + * Kill the timer if it is running. does nothing otherwise + */ + private synchronized void killTimer() { + if (terminator != null) { + terminator.shutdown(false); + } + } + + + /** + * This is an override point; it is where subclasses get to change their workflow + * depending on what happens underneath. + * It is only called outside of component termination, i.e. when {@link #isWorkflowTerminating()} is + * false, and when the comp parameter is a child, that is <code>sfContainsChild(comp)</code> holds. + * If the the method returns true, the event is forwarded up the object heirarchy, which + * will eventually trigger a component termination. + * <p/> + * Always return false if you start new components from this method! + * </p> + * + * @param status exit record of the component + * @param comp child component that is terminating + * @return true if the termination event is to be forwarded up the chain. + */ + protected boolean onChildTerminated(TerminationRecord status, Prim comp) { + killTimer(); + return true; + } + +/* this is the old runnable factored out to show what the logic was. + as of 3.10.0 it was broken to the extent that if time<= 0, the terminator +... [truncated message content] |