You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kw...@apache.org on 2015/08/09 10:13:07 UTC

svn commit: r1694857 - in /qpid/java/trunk/broker-core/src: main/java/org/apache/qpid/server/model/ test/java/org/apache/qpid/server/model/testmodels/hierarchy/

Author: kwall
Date: Sun Aug  9 08:13:07 2015
New Revision: 1694857

URL: http://svn.apache.org/r1694857
Log:
QPID-6684: [Java Broker] Ensure asynchronous state change exceptions are passed back to caller

* Guarded case 1 - attainState returns a Future containing an exception
* Guarded case 2 - the recursive call to doAttainState returned a future contain a non-RuntimeException
* Refactored to use Futures.allAsList (rather than ChildCounter)
* Added supporting unit test

Modified:
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/AbstractConfiguredObject.java
    qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/AbstractConfiguredObjectTest.java
    qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractEngineImpl.java
    qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestEngine.java
    qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestKitCarImpl.java
    qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestStandardCarImpl.java

Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/AbstractConfiguredObject.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/AbstractConfiguredObject.java?rev=1694857&r1=1694856&r2=1694857&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/AbstractConfiguredObject.java (original)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/AbstractConfiguredObject.java Sun Aug  9 08:13:07 2015
@@ -542,30 +542,6 @@ public abstract class AbstractConfigured
         }
     }
 
-    private class ChildCounter
-    {
-        private final AtomicInteger _count = new AtomicInteger();
-        private final Runnable _task;
-
-        private ChildCounter(final Runnable task)
-        {
-            _task = task;
-        }
-
-        public void incrementCount()
-        {
-            _count.incrementAndGet();
-        }
-
-        public void decrementCount()
-        {
-            if(_count.decrementAndGet() == 0)
-            {
-                _task.run();
-            }
-        }
-    }
-
     protected final ListenableFuture<Void> closeChildren()
     {
         final List<ListenableFuture<Void>> childCloseFutures = new ArrayList<>();
@@ -587,9 +563,9 @@ public abstract class AbstractConfigured
                     public void onFailure(final Throwable t)
                     {
                         LOGGER.error("Exception occurred while closing {} : {}",
-                                     new Object[] {child.getClass().getSimpleName(),
-                                     child.getName(),
-                                     t});
+                                     new Object[]{child.getClass().getSimpleName(),
+                                             child.getName(),
+                                             t});
                     }
                 });
                 childCloseFutures.add(childCloseFuture);
@@ -795,22 +771,63 @@ public abstract class AbstractConfigured
 
     private ListenableFuture<Void> doAttainState(final AbstractConfiguredObjectExceptionHandler exceptionHandler)
     {
+        final List<ListenableFuture<Void>> childStateFutures = new ArrayList<>();
+
+        applyToChildren(new Action<ConfiguredObject<?>>()
+        {
+            @Override
+            public void performAction(final ConfiguredObject<?> child)
+            {
+                if (child instanceof AbstractConfiguredObject
+                    && ((AbstractConfiguredObject)child)._dynamicState.get() == DynamicState.OPENED)
+                {
+                    final AbstractConfiguredObject configuredObject = (AbstractConfiguredObject) child;
+                    childStateFutures.add(configuredObject.doAttainState(exceptionHandler));
+
+                }
+            }
+        });
+
+        ListenableFuture<List<Void>> combinedChildStateFuture = Futures.allAsList(childStateFutures);
+
         final SettableFuture<Void> returnVal = SettableFuture.create();
-        final ChildCounter counter = new ChildCounter(new Runnable()
+        Futures.addCallback(combinedChildStateFuture, new FutureCallback<List<Void>>()
         {
             @Override
-            public void run()
+            public void onSuccess(final List<Void> result)
             {
                 try
                 {
-                    attainState().addListener(new Runnable()
-                    {
-                        @Override
-                        public void run()
-                        {
-                            returnVal.set(null);
-                        }
-                    }, getTaskExecutor().getExecutor());
+                    Futures.addCallback(attainState(),
+                                        new FutureCallback<Void>()
+                                        {
+                                            @Override
+                                            public void onSuccess(final Void result1)
+                                            {
+                                                returnVal.set(null);
+                                            }
+
+                                            @Override
+                                            public void onFailure(final Throwable t)
+                                            {
+                                                try
+                                                {
+                                                    if (t instanceof RuntimeException)
+                                                    {
+                                                        exceptionHandler.handleException((RuntimeException) t,
+                                                                                         AbstractConfiguredObject.this);
+                                                        returnVal.set(null);
+                                                    }
+                                                }
+                                                finally
+                                                {
+                                                    if (!returnVal.isDone())
+                                                    {
+                                                        returnVal.setException(t);
+                                                    }
+                                                }
+                                            }
+                                        },  getTaskExecutor().getExecutor());
                 }
                 catch(RuntimeException e)
                 {
@@ -825,51 +842,15 @@ public abstract class AbstractConfigured
                     }
                 }
             }
-        });
-        counter.incrementCount();
-        applyToChildren(new Action<ConfiguredObject<?>>()
-        {
+
             @Override
-            public void performAction(final ConfiguredObject<?> child)
+            public void onFailure(final Throwable t)
             {
-                if (child instanceof AbstractConfiguredObject)
-                {
-                    final AbstractConfiguredObject configuredObject = (AbstractConfiguredObject) child;
-                    if (configuredObject._dynamicState.get() == DynamicState.OPENED)
-                    {
-                        counter.incrementCount();
-                        Futures.addCallback(configuredObject.doAttainState(exceptionHandler),
-                                            new FutureCallback()
-                                            {
-                                                @Override
-                                                public void onSuccess(final Object result)
-                                                {
-                                                    counter.decrementCount();
-                                                }
-
-                                                @Override
-                                                public void onFailure(final Throwable t)
-                                                {
-                                                    try
-                                                    {
-                                                        if (t instanceof RuntimeException)
-                                                        {
-                                                            exceptionHandler.handleException((RuntimeException) t,
-                                                                                             configuredObject);
-                                                        }
-                                                    }
-                                                    finally
-                                                    {
-                                                        counter.decrementCount();
-                                                    }
-                                                }
-                                            },getTaskExecutor().getExecutor());
-
-                    }
-                }
+                // One or more children failed to attain state but the error could not be handled by the handler
+                returnVal.setException(t);
             }
         });
-        counter.decrementCount();
+
         return returnVal;
     }
 

Modified: qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/AbstractConfiguredObjectTest.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/AbstractConfiguredObjectTest.java?rev=1694857&r1=1694856&r2=1694857&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/AbstractConfiguredObjectTest.java (original)
+++ qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/AbstractConfiguredObjectTest.java Sun Aug  9 08:13:07 2015
@@ -19,10 +19,14 @@
 
 package org.apache.qpid.server.model.testmodels.hierarchy;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
+import java.util.concurrent.ExecutionException;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
@@ -31,6 +35,8 @@ import org.apache.qpid.server.configurat
 import org.apache.qpid.server.model.AbstractConfiguredObject;
 import org.apache.qpid.server.model.ConfiguredObject;
 import org.apache.qpid.server.model.Model;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.SystemConfig;
 import org.apache.qpid.server.store.ConfiguredObjectRecord;
 import org.apache.qpid.test.utils.QpidTestCase;
 
@@ -102,18 +108,11 @@ public class AbstractConfiguredObjectTes
 
     public void testGetChildren_NewChild()
     {
-        final String carName = "myCar";
-        Map<String, Object> carAttributes = new HashMap<>();
-        carAttributes.put(ConfiguredObject.NAME, carName);
-        carAttributes.put(ConfiguredObject.TYPE, TestKitCarImpl.TEST_KITCAR_TYPE);
-
-        TestCar car = _model.getObjectFactory().create(TestCar.class, carAttributes);
-
+        TestCar car = _model.getObjectFactory().create(TestCar.class, Collections.<String, Object>singletonMap(ConfiguredObject.NAME, "myCar"));
 
         String engineName = "myEngine";
         Map<String, Object> engineAttributes = new HashMap<>();
         engineAttributes.put(ConfiguredObject.NAME, engineName);
-        engineAttributes.put(ConfiguredObject.TYPE, TestElecEngineImpl.TEST_ELEC_ENGINE_TYPE);
 
         TestEngine engine = (TestEngine) car.createChild(TestEngine.class, engineAttributes);
 
@@ -130,51 +129,12 @@ public class AbstractConfiguredObjectTes
 
     public void testGetChildren_RecoveredChild() throws Exception
     {
-        final String carName = "myCar";
-        Map<String, Object> carAttributes = new HashMap<>();
-        carAttributes.put(ConfiguredObject.NAME, carName);
-        carAttributes.put(ConfiguredObject.TYPE, TestKitCarImpl.TEST_KITCAR_TYPE);
-
-        final TestCar car = _model.getObjectFactory().create(TestCar.class, carAttributes);
-
-        String engineName = "myEngine";
-        final Map<String, Object> engineAttributes = new HashMap<>();
-        engineAttributes.put(ConfiguredObject.NAME, engineName);
-        engineAttributes.put(ConfiguredObject.TYPE, TestElecEngineImpl.TEST_ELEC_ENGINE_TYPE);
-
-        ConfiguredObjectRecord engineCor = new ConfiguredObjectRecord()
-        {
-            @Override
-            public UUID getId()
-            {
-                return UUID.randomUUID();
-            }
-
-            @Override
-            public String getType()
-            {
-                return TestEngine.class.getSimpleName();
-            }
-
-            @Override
-            public Map<String, Object> getAttributes()
-            {
-                return engineAttributes;
-            }
-
-            @Override
-            public Map<String, UUID> getParents()
-            {
-                return Collections.singletonMap(TestCar.class.getSimpleName(), car.getId());
-            }
-        };
-
-        // Recover and resolve the child.  Resolving the child registers the child with its parent (car),
-        // but the child is not open, so won't have attained state
-        TestEngine engine = (TestEngine) _model.getObjectFactory().recover(engineCor, car).resolve();
+        final TestCar car = recoverParentAndChild();
 
         // Check we can observe the recovered child from the parent
         assertEquals(1, car.getChildren(TestEngine.class).size());
+        TestEngine engine = (TestEngine) car.getChildren(TestEngine.class).iterator().next();
+
         assertEquals(engine, car.getChildById(TestEngine.class, engine.getId()));
         assertEquals(engine, car.getChildByName(TestEngine.class, engine.getName()));
 
@@ -182,30 +142,156 @@ public class AbstractConfiguredObjectTes
         assertNotNull(attainedChild);
         assertFalse("Engine should not have yet attained state", attainedChild.isDone());
 
-        engine.open();
+        car.open();
 
         assertTrue("Engine should have now attained state", attainedChild.isDone());
         assertEquals(engine, attainedChild.get());
     }
 
-    public void testCloseAwaitsChildCloseCompletion()
+    public void testOpenAwaitsChildToAttainState() throws Exception
     {
-        final String carName = "myCar";
-        Map<String, Object> carAttributes = new HashMap<>();
-        carAttributes.put(ConfiguredObject.NAME, carName);
-        carAttributes.put(ConfiguredObject.TYPE, TestKitCarImpl.TEST_KITCAR_TYPE);
+        SettableFuture<Void> engineStateChangeControllingFuture = SettableFuture.create();
 
-        TestCar car = _model.getObjectFactory().create(TestCar.class, carAttributes);
+        final TestCar car = recoverParentAndChild();
+
+        // Check we can observe the recovered child from the parent
+        assertEquals(1, car.getChildren(TestEngine.class).size());
+        TestEngine engine = (TestEngine) car.getChildren(TestEngine.class).iterator().next();
+        engine.setStateChangeFuture(engineStateChangeControllingFuture);
+
+        ListenableFuture carFuture = car.openAsync();
+        assertFalse("car open future has completed before engine has attained state", carFuture.isDone());
+
+        engineStateChangeControllingFuture.set(null);
+
+        assertTrue("car open future has not completed", carFuture.isDone());
+        carFuture.get();
+    }
+
+    public void testOpenAwaitsChildToAttainState_ChildStateChangeAsyncErrors() throws Exception
+    {
+        SettableFuture<Void> engineStateChangeControllingFuture = SettableFuture.create();
+
+        final TestCar car = recoverParentAndChild();
+
+        // Check we can observe the recovered child from the parent
+        assertEquals(1, car.getChildren(TestEngine.class).size());
+        TestEngine engine = (TestEngine) car.getChildren(TestEngine.class).iterator().next();
+        engine.setStateChangeFuture(engineStateChangeControllingFuture);
+
+        ListenableFuture carFuture = car.openAsync();
+        assertFalse("car open future has completed before engine has attained state", carFuture.isDone());
+
+        engineStateChangeControllingFuture.setException(new RuntimeException("child attain state exception"));
+
+        assertTrue("car open future has not completed", carFuture.isDone());
+        carFuture.get();
+
+        assertEquals(State.ERRORED, engine.getState());
+    }
+
+    public void testOpenAwaitsChildToAttainState_ChildStateChangeSyncErrors() throws Exception
+    {
+        final TestCar car = recoverParentAndChild();
+
+        // Check we can observe the recovered child from the parent
+        assertEquals(1, car.getChildren(TestEngine.class).size());
+        TestEngine engine = (TestEngine) car.getChildren(TestEngine.class).iterator().next();
+
+        engine.setStateChangeException(new RuntimeException("child attain state exception"));
+
+        ListenableFuture carFuture = car.openAsync();
+
+        assertTrue("car open future has not completed", carFuture.isDone());
+        carFuture.get();
+
+        assertEquals(State.ERRORED, engine.getState());
+    }
+
+    public void testCreateAwaitsAttainState()
+    {
+        SettableFuture stateChangeFuture = SettableFuture.create();
+
+        TestCar car = _model.getObjectFactory().create(TestCar.class, Collections.<String, Object>singletonMap(ConfiguredObject.NAME, "myCar"));
+
+        Map<String, Object> engineAttributes = new HashMap<>();
+        engineAttributes.put(ConfiguredObject.NAME, "myEngine");
+        engineAttributes.put(TestEngine.STATE_CHANGE_FUTURE, stateChangeFuture);
+
+        ListenableFuture engine = car.createChildAsync(TestEngine.class, engineAttributes);
+        assertFalse("create child has completed before state change completes", engine.isDone());
+
+        stateChangeFuture.set(null);
+
+        assertTrue("create child has not completed", engine.isDone());
+    }
+
+    public void testCreateAwaitsAttainState_StateChangeAsyncErrors() throws Exception
+    {
+        SettableFuture stateChangeFuture = SettableFuture.create();
+        RuntimeException stateChangeException = new RuntimeException("state change error");
+
+        TestCar car = _model.getObjectFactory().create(TestCar.class, Collections.<String, Object>singletonMap(ConfiguredObject.NAME, "myCar"));
 
+        Map<String, Object> engineAttributes = new HashMap<>();
+        engineAttributes.put(ConfiguredObject.NAME, "myEngine");
+        engineAttributes.put(TestEngine.STATE_CHANGE_FUTURE, stateChangeFuture);
+
+        ListenableFuture engine = car.createChildAsync(TestEngine.class, engineAttributes);
+        assertFalse("create child has completed before state change completes", engine.isDone());
+
+        stateChangeFuture.setException(stateChangeException);
+
+        assertTrue("create child has not completed", engine.isDone());
+        try
+        {
+            engine.get();
+            fail("Exception not thrown");
+        }
+        catch (ExecutionException ee)
+        {
+            assertSame(stateChangeException, ee.getCause());
+        }
+    }
+
+    public void testCreateAwaitsAttainState_StateChangeSyncErrors() throws Exception
+    {
+        RuntimeException stateChangeException = new RuntimeException("state change error");
+
+        TestCar car = _model.getObjectFactory().create(TestCar.class, Collections.<String, Object>singletonMap(ConfiguredObject.NAME, "myCar"));
+
+        Map<String, Object> engineAttributes = new HashMap<>();
+        engineAttributes.put(ConfiguredObject.NAME, "myEngine");
+        engineAttributes.put(TestEngine.STATE_CHANGE_EXCEPTION, stateChangeException);
+
+        ListenableFuture engine = car.createChildAsync(TestEngine.class, engineAttributes);
+        assertTrue("create child has not completed", engine.isDone());
+
+        try
+        {
+            engine.get();
+            fail("Exception not thrown");
+        }
+        catch (ExecutionException ee)
+        {
+            assertSame(stateChangeException, ee.getCause());
+        }
+    }
+
+    public void testCloseAwaitsChildCloseCompletion()
+    {
         SettableFuture<Void> engineCloseControllingFuture = SettableFuture.create();
 
+        TestCar car = _model.getObjectFactory().create(TestCar.class,
+                                                       Collections.<String, Object>singletonMap(ConfiguredObject.NAME,
+                                                                                                "myCar"));
+
         String engineName = "myEngine";
         Map<String, Object> engineAttributes = new HashMap<>();
         engineAttributes.put(ConfiguredObject.NAME, engineName);
-        engineAttributes.put(ConfiguredObject.TYPE, TestElecEngineImpl.TEST_ELEC_ENGINE_TYPE);
+        engineAttributes.put(TestEngine.BEFORE_CLOSE_FUTURE, engineCloseControllingFuture);
 
         TestEngine engine = (TestEngine) car.createChild(TestEngine.class, engineAttributes);
-        engine.setBeforeCloseFuture(engineCloseControllingFuture);
 
         ListenableFuture carListenableFuture = car.closeAsync();
         assertFalse("car close future has completed before engine closed", carListenableFuture.isDone());
@@ -318,5 +404,82 @@ public class AbstractConfiguredObjectTes
         assertSame(engine, car.getChildByName(TestEngine.class, engine.getName()));
     }
 
+    /** Simulates recovery of a parent/child from a store.  Neither will be open yet. */
+    private TestCar recoverParentAndChild()
+    {
+        final SystemConfig mockSystemConfig = mock(SystemConfig.class);
+        when(mockSystemConfig.getId()).thenReturn(UUID.randomUUID());
+        when(mockSystemConfig.getModel()).thenReturn(TestModel.getInstance());
+
+        final String carName = "myCar";
+        final Map<String, Object> carAttributes = new HashMap<>();
+        carAttributes.put(ConfiguredObject.NAME, carName);
+        carAttributes.put(ConfiguredObject.TYPE, TestKitCarImpl.TEST_KITCAR_TYPE);
+
+        ConfiguredObjectRecord carCor = new ConfiguredObjectRecord()
+        {
+            @Override
+            public UUID getId()
+            {
+                return UUID.randomUUID();
+            }
+
+            @Override
+            public String getType()
+            {
+                return TestCar.class.getSimpleName();
+            }
+
+            @Override
+            public Map<String, Object> getAttributes()
+            {
+                return carAttributes;
+            }
+
+            @Override
+            public Map<String, UUID> getParents()
+            {
+                return Collections.singletonMap(SystemConfig.class.getSimpleName(), mockSystemConfig.getId());
+            }
+        };
+
+        final TestCar car = (TestCar) _model.getObjectFactory().recover(carCor, mockSystemConfig).resolve();
+
+        String engineName = "myEngine";
+        final Map<String, Object> engineAttributes = new HashMap<>();
+        engineAttributes.put(ConfiguredObject.NAME, engineName);
+        engineAttributes.put(ConfiguredObject.TYPE, TestElecEngineImpl.TEST_ELEC_ENGINE_TYPE);
+
+        ConfiguredObjectRecord engineCor = new ConfiguredObjectRecord()
+        {
+            @Override
+            public UUID getId()
+            {
+                return UUID.randomUUID();
+            }
+
+            @Override
+            public String getType()
+            {
+                return TestEngine.class.getSimpleName();
+            }
+
+            @Override
+            public Map<String, Object> getAttributes()
+            {
+                return engineAttributes;
+            }
+
+            @Override
+            public Map<String, UUID> getParents()
+            {
+                return Collections.singletonMap(TestCar.class.getSimpleName(), car.getId());
+            }
+        };
+
+        _model.getObjectFactory().recover(engineCor, car).resolve();
+        return car;
+    }
+
 
 }

Modified: qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractEngineImpl.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractEngineImpl.java?rev=1694857&r1=1694856&r2=1694857&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractEngineImpl.java (original)
+++ qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractEngineImpl.java Sun Aug  9 08:13:07 2015
@@ -28,11 +28,21 @@ import com.google.common.util.concurrent
 
 import org.apache.qpid.server.model.AbstractConfiguredObject;
 import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ManagedAttributeField;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.StateTransition;
 
 public class TestAbstractEngineImpl<X extends TestAbstractEngineImpl<X>> extends AbstractConfiguredObject<X> implements TestEngine<X>
 {
+    @ManagedAttributeField
     private ListenableFuture<Void> _beforeCloseFuture = Futures.immediateFuture(null);
 
+    @ManagedAttributeField
+    private Object _stateChangeFuture = Futures.immediateFuture(null);
+
+    @ManagedAttributeField
+    private RuntimeException _stateChangeException;
+
     public TestAbstractEngineImpl(final Map<Class<? extends ConfiguredObject>, ConfiguredObject<?>> parents,
                                   final Map<String, Object> attributes)
     {
@@ -40,14 +50,56 @@ public class TestAbstractEngineImpl<X ex
     }
 
     @Override
-    public void setBeforeCloseFuture(final ListenableFuture listenableFuture)
+    public Object getBeforeCloseFuture()
+    {
+        return _beforeCloseFuture;
+    }
+
+    @Override
+    public void setBeforeCloseFuture(final ListenableFuture<Void> listenableFuture)
     {
         _beforeCloseFuture = listenableFuture;
     }
 
     @Override
+    public Object getStateChangeFuture()
+    {
+        return _stateChangeFuture;
+    }
+
+    @Override
+    public void setStateChangeFuture(final ListenableFuture<Void> listenableFuture)
+    {
+        _stateChangeFuture = listenableFuture;
+    }
+
+
+    @Override
+    public Object getStateChangeException()
+    {
+        return _stateChangeException;
+    }
+
+    @Override
+    public void setStateChangeException(final RuntimeException exception)
+    {
+        _stateChangeException = exception;
+    }
+
+    @Override
     protected ListenableFuture<Void> beforeClose()
     {
         return _beforeCloseFuture;
     }
+
+    @StateTransition(currentState = {State.UNINITIALIZED, State.ERRORED}, desiredState = State.ACTIVE)
+    private ListenableFuture<Void> onActivate()
+    {
+        RuntimeException stateChangeException = _stateChangeException;
+        if (stateChangeException != null)
+        {
+            throw stateChangeException;
+        }
+        return (ListenableFuture<Void>) _stateChangeFuture;
+    }
 }

Modified: qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestEngine.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestEngine.java?rev=1694857&r1=1694856&r2=1694857&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestEngine.java (original)
+++ qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestEngine.java Sun Aug  9 08:13:07 2015
@@ -21,13 +21,36 @@
 package org.apache.qpid.server.model.testmodels.hierarchy;
 
 import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
 
 import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ManagedAttribute;
 import org.apache.qpid.server.model.ManagedObject;
 
-@ManagedObject(category = true)
+@ManagedObject(category = true, defaultType = TestElecEngineImpl.TEST_ELEC_ENGINE_TYPE)
 public interface TestEngine<X extends TestEngine<X>> extends ConfiguredObject<X>
 {
-    /** Injectable close future, used to control when/how close occurs during test */
-    void setBeforeCloseFuture(ListenableFuture listenableFuture);
+    String BEFORE_CLOSE_FUTURE = "beforeCloseFuture";
+    String STATE_CHANGE_FUTURE = "stateChangeFuture";
+    String STATE_CHANGE_EXCEPTION = "stateChangeException";
+
+    /* Injectable close future, used to control when/how close completes during the test */
+    @ManagedAttribute
+    Object getBeforeCloseFuture();
+
+    void setBeforeCloseFuture(ListenableFuture<Void> listenableFuture);
+
+    /* Injectable state change future, used to control when/how asynch state transition completes during the test */
+
+    @ManagedAttribute
+    Object getStateChangeFuture();
+
+    void setStateChangeFuture(ListenableFuture<Void> listenableFuture);
+
+    /* Injectable exception, used to introduce an exception into a state change transition */
+
+    @ManagedAttribute
+    Object getStateChangeException();
+    void setStateChangeException(RuntimeException exception);
+
 }

Modified: qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestKitCarImpl.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestKitCarImpl.java?rev=1694857&r1=1694856&r2=1694857&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestKitCarImpl.java (original)
+++ qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestKitCarImpl.java Sun Aug  9 08:13:07 2015
@@ -20,6 +20,8 @@ package org.apache.qpid.server.model.tes
 
 import java.util.Map;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
 import org.apache.qpid.server.configuration.updater.CurrentThreadTaskExecutor;
 import org.apache.qpid.server.model.AbstractConfiguredObject;
 import org.apache.qpid.server.model.ConfiguredObject;
@@ -51,11 +53,11 @@ public class TestKitCarImpl extends Abst
     }
 
     @Override
-    public <C extends ConfiguredObject> C createChild(final Class<C> childClass,
-                                                      final Map<String, Object> attributes,
-                                                      final ConfiguredObject... otherParents)
+    protected <C extends ConfiguredObject> ListenableFuture<C> addChildAsync(final Class<C> childClass,
+                                                                             final Map<String, Object> attributes,
+                                                                             final ConfiguredObject... otherParents)
     {
-        return (C) getObjectFactory().create(childClass, attributes, this);
+        return getObjectFactory().createAsync(childClass, attributes, this);
     }
 
     private static CurrentThreadTaskExecutor newTaskExecutor()

Modified: qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestStandardCarImpl.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestStandardCarImpl.java?rev=1694857&r1=1694856&r2=1694857&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestStandardCarImpl.java (original)
+++ qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestStandardCarImpl.java Sun Aug  9 08:13:07 2015
@@ -25,8 +25,11 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
 import org.apache.qpid.server.configuration.updater.CurrentThreadTaskExecutor;
 import org.apache.qpid.server.model.AbstractConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObject;
 import org.apache.qpid.server.model.ManagedObject;
 import org.apache.qpid.server.model.ManagedObjectFactoryConstructor;
 import org.apache.qpid.server.model.testmodels.TestSecurityManager;
@@ -55,6 +58,14 @@ public class TestStandardCarImpl extends
         return currentThreadTaskExecutor;
     }
 
+    @Override
+    protected <C extends ConfiguredObject> ListenableFuture<C> addChildAsync(final Class<C> childClass,
+                                                                             final Map<String, Object> attributes,
+                                                                             final ConfiguredObject... otherParents)
+    {
+        return getObjectFactory().createAsync(childClass, attributes, this);
+    }
+
     @SuppressWarnings("unused")
     public static Map<String, Collection<String>> getSupportedChildTypes()
     {



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org