blob: 8aa60cfc5a943d124260dba1eb11dc685a359cf1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.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;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
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;
/**
* Tests behaviour of AbstractConfiguredObjects when hierarchies of objects are used together.
* Responsibilities to include adding/removing of children and correct firing of listeners.
*/
public class AbstractConfiguredObjectTest extends QpidTestCase
{
private final Model _model = TestModel.getInstance();
public void testCreateCategoryDefault()
{
final String objectName = "testCreateCategoryDefault";
Map<String, Object> attributes = Collections.<String, Object>singletonMap(ConfiguredObject.NAME, objectName);
TestCar object = _model.getObjectFactory().create(TestCar.class, attributes);
assertEquals(objectName, object.getName());
assertEquals(TestStandardCarImpl.TEST_STANDARD_CAR_TYPE, object.getType());
assertTrue(object instanceof TestStandardCar);
}
public void testCreateUnrecognisedType()
{
final String objectName = "testCreateCategoryDefault";
Map<String, Object> attributes = new HashMap<>();
attributes.put(ConfiguredObject.NAME, objectName);
attributes.put(ConfiguredObject.TYPE, "notatype");
try
{
_model.getObjectFactory().create(TestCar.class, attributes);
fail("Exception not thrown");
}
catch (IllegalConfigurationException ice)
{
// PASS
}
}
public void testCreateCarWithEngine()
{
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);
assertEquals(carName, car.getName());
assertEquals(0, car.getChildren(TestEngine.class).size());
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);
assertEquals(1, car.getChildren(TestEngine.class).size());
assertEquals(engineName, engine.getName());
assertEquals(TestElecEngineImpl.TEST_ELEC_ENGINE_TYPE, engine.getType());
}
public void testGetChildren_NewChild()
{
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);
TestEngine engine = (TestEngine) car.createChild(TestEngine.class, engineAttributes);
// Check we can observe the new child from the parent
assertEquals(1, car.getChildren(TestEngine.class).size());
assertEquals(engine, car.getChildById(TestEngine.class, engine.getId()));
assertEquals(engine, car.getChildByName(TestEngine.class, engine.getName()));
ListenableFuture attainedChild = car.getAttainedChildByName(TestEngine.class, engine.getName());
assertNotNull(attainedChild);
assertTrue("Engine should have already attained state", attainedChild.isDone());
}
public void testGetChildren_RecoveredChild() 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();
assertEquals(engine, car.getChildById(TestEngine.class, engine.getId()));
assertEquals(engine, car.getChildByName(TestEngine.class, engine.getName()));
ListenableFuture attainedChild = car.getAttainedChildByName(TestEngine.class, engine.getName());
assertNotNull(attainedChild);
assertFalse("Engine should not have yet attained state", attainedChild.isDone());
car.open();
assertTrue("Engine should have now attained state", attainedChild.isDone());
assertEquals(engine, attainedChild.get());
}
public void testOpenAwaitsChildToAttainState() 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.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(TestEngine.BEFORE_CLOSE_FUTURE, engineCloseControllingFuture);
TestEngine engine = (TestEngine) car.createChild(TestEngine.class, engineAttributes);
ListenableFuture carListenableFuture = car.closeAsync();
assertFalse("car close future has completed before engine closed", carListenableFuture.isDone());
assertSame("engine deregistered from car too early", engine, car.getChildById(TestEngine.class, engine.getId()));
engineCloseControllingFuture.set(null);
assertTrue("car close future has not completed", carListenableFuture.isDone());
assertNull("engine not deregistered", car.getChildById(TestEngine.class, engine.getId()));
}
public void testDefaultContextVariableWhichRefersToAncestor()
{
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);
assertEquals(carName, car.getName());
assertEquals(0, car.getChildren(TestEngine.class).size());
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);
assertTrue("context var not in contextKeys",
car.getContextKeys(true).contains(TestCar.TEST_CONTEXT_VAR));
String expected = "a value " + carName;
assertEquals(expected, car.getContextValue(String.class, TestCar.TEST_CONTEXT_VAR));
assertTrue("context var not in contextKeys",
engine.getContextKeys(true).contains(TestCar.TEST_CONTEXT_VAR));
assertEquals(expected, engine.getContextValue(String.class, TestCar.TEST_CONTEXT_VAR));
}
public void testUserPreferencesCreatedOnEngineCreation()
{
Map<String, Object> carAttributes = new HashMap<>();
carAttributes.put(ConfiguredObject.NAME, "myCar");
carAttributes.put(ConfiguredObject.TYPE, TestStandardCarImpl.TEST_STANDARD_CAR_TYPE);
TestCar car = _model.getObjectFactory().create(TestCar.class, carAttributes);
Map<String, Object> engineAttributes = new HashMap<>();
engineAttributes.put(ConfiguredObject.NAME, "myEngine");
engineAttributes.put(ConfiguredObject.TYPE, TestElecEngineImpl.TEST_ELEC_ENGINE_TYPE);
TestEngine engine = (TestEngine) car.createChild(TestEngine.class, engineAttributes);
assertNotNull("Unexpected user preferences", engine.getUserPreferences());
}
public void testDuplicateChildRejected_Name()
{
doDuplicateChildCheck(ConfiguredObject.NAME);
}
public void testDuplicateChildRejected_Id()
{
doDuplicateChildCheck(ConfiguredObject.ID);
}
private void doDuplicateChildCheck(final String attrToDuplicate)
{
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);
assertEquals(0, car.getChildren(TestEngine.class).size());
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);
assertEquals(1, car.getChildren(TestEngine.class).size());
Map<String, Object> secondEngineNameAttribute = new HashMap<>();
secondEngineNameAttribute.put(ConfiguredObject.TYPE, TestPetrolEngineImpl.TEST_PETROL_ENGINE_TYPE);
if (attrToDuplicate.equals(ConfiguredObject.NAME))
{
String secondEngineName = "myEngine";
secondEngineNameAttribute.put(ConfiguredObject.NAME, secondEngineName);
}
else
{
String secondEngineName = "myPetrolEngine";
secondEngineNameAttribute.put(ConfiguredObject.ID, engine.getId());
secondEngineNameAttribute.put(ConfiguredObject.NAME, secondEngineName);
}
try
{
car.createChild(TestEngine.class, secondEngineNameAttribute);
fail("exception not thrown");
}
catch (AbstractConfiguredObject.DuplicateNameException dne)
{
assertEquals(ConfiguredObject.NAME, attrToDuplicate);
// PASS
}
catch (AbstractConfiguredObject.DuplicateIdException die)
{
// PASS
assertEquals(ConfiguredObject.ID, attrToDuplicate);
}
assertEquals("Unexpected number of children after rejected duplicate", 1, car.getChildren(TestEngine.class).size());
assertSame(engine, car.getChildById(TestEngine.class, engine.getId()));
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;
}
}