/*
* RHQ Management Platform
* Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.core.pc.inventory;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.io.File;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.rhq.core.clientapi.agent.metadata.PluginMetadataManager;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.pc.PluginContainer;
import org.rhq.core.pc.PluginContainerConfiguration;
import org.rhq.core.pc.component.ComponentInvocationContextImpl;
import org.rhq.core.pc.util.FacetLockType;
import org.rhq.core.pluginapi.availability.AvailabilityFacet;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
/**
* Unit test for {@link ResourceContainer}.
*
* @author Ian Springer
*/
@Test
public class ResourceContainerTest {
private static final Log LOG = LogFactory.getLog(ResourceContainerTest.class);
@BeforeClass
protected void beforeClass() {
PluginContainerConfiguration config = new PluginContainerConfiguration();
File dataDir = new File("target/PluginContainerTest");
dataDir.mkdirs();
config.setDataDirectory(dataDir);
PluginContainer pc = PluginContainer.getInstance();
pc.setConfiguration(config);
pc.initialize();
}
@AfterClass
protected void afterClass() {
PluginContainer.getInstance().shutdown();
}
public void testCreateResourceComponentProxy() throws Exception {
ResourceContainer resourceContainer = getResourceContainer();
if (LOG.isDebugEnabled()) {
LOG.debug("Testing proxy call that should timeout...");
}
AvailabilityFacet resourceComponentProxy = resourceContainer.createResourceComponentProxy(
AvailabilityFacet.class, FacetLockType.NONE, 1000L, true, false, true);
try {
resourceComponentProxy.getAvailability();
assert (false);
} catch (RuntimeException e) {
assert (e instanceof TimeoutException) : "[" + e + "] is not a org.rhq.core.pc.inventory.TimeoutException.";
}
if (LOG.isDebugEnabled()) {
LOG.debug("SUCCESS!");
}
if (LOG.isDebugEnabled()) {
LOG.debug("Testing proxy call that should complete successfully...");
}
resourceComponentProxy = resourceContainer.createResourceComponentProxy(AvailabilityFacet.class,
FacetLockType.NONE, 2000L, true, false, true);
AvailabilityType avail = resourceComponentProxy.getAvailability();
assert (avail == AvailabilityType.UP);
if (LOG.isDebugEnabled()) {
LOG.debug("SUCCESS!");
}
if (LOG.isDebugEnabled()) {
LOG.debug("Testing proxy call that should fail...");
}
ResourceComponent naughtyResourceComponent = new MockResourceComponent(true);
resourceContainer.setResourceComponent(naughtyResourceComponent);
resourceComponentProxy = resourceContainer.createResourceComponentProxy(AvailabilityFacet.class,
FacetLockType.NONE, Long.MAX_VALUE, true, false, true);
try {
resourceComponentProxy.getAvailability();
assert (false);
} catch (RuntimeException e) {
assert (e instanceof MockRuntimeException);
}
if (LOG.isDebugEnabled()) {
LOG.debug("SUCCESS!");
}
if (LOG.isDebugEnabled()) {
LOG.debug("Testing proxy call should not timeout; its not to a declared method in proxied interface");
}
resourceComponentProxy = resourceContainer.createResourceComponentProxy(AvailabilityFacet.class,
FacetLockType.NONE, 1000L, true, false, true);
String string = resourceComponentProxy.toString();
assert (string.equals(MockResourceComponent.class.toString()));
if (LOG.isDebugEnabled()) {
LOG.debug("SUCCESS!");
}
// TODO: Test proxy locking.
}
public void testInterruptedComponentInvocationContext() throws Exception {
ResourceContainer resourceContainer = getResourceContainer();
OperationFacet proxy = resourceContainer.createResourceComponentProxy(OperationFacet.class,
FacetLockType.WRITE, SECONDS.toMillis(1L), true, false, true);
try {
proxy.invokeOperation("op", new Configuration());
fail("Expected invokeOperation to throw a TimeoutException");
} catch (TimeoutException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Caught expected TimeoutException: " + e.getMessage());
}
MockResourceComponent component = (MockResourceComponent) resourceContainer.getResourceComponent();
for (int i = 0; i < 5 && !component.caughtInterruptedComponentInvocation(); i++) {
Thread.sleep(SECONDS.toMillis(2));
}
assertTrue(component.caughtInterruptedComponentInvocation());
}
}
public void testUninterruptedComponentInvocationContext() throws Exception {
ResourceContainer resourceContainer = getResourceContainer();
OperationFacet proxy = resourceContainer.createResourceComponentProxy(OperationFacet.class,
FacetLockType.WRITE, SECONDS.toMillis(10L), true, false, true);
try {
OperationResult op = proxy.invokeOperation("op", new Configuration());
assertTrue(op.getSimpleResult().equals(MockResourceComponent.OPERATION_RESULT));
} catch (Exception e) {
fail("Caught unexpected Exception: " + e.getClass().getName());
assertFalse(((MockResourceComponent) resourceContainer.getResourceComponent())
.caughtInterruptedComponentInvocation());
}
}
private ResourceContainer getResourceContainer() throws Exception {
Resource resource = new Resource("TestPlatformKey", "MyTestPlatform", PluginMetadataManager.TEST_PLATFORM_TYPE);
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
ResourceContainer resourceContainer = new ResourceContainer(resource, contextClassLoader);
ResourceContext resourceContext = new ResourceContext(resource, null, null, null, null, null, null, null, null,
null, null, null, null, null, new ComponentInvocationContextImpl());
resourceContainer.setResourceContext(resourceContext);
ResourceComponent resourceComponent = new MockResourceComponent(false);
resourceContainer.setResourceComponent(resourceComponent);
resourceComponent.start(resourceContext);
return resourceContainer;
}
private class MockResourceComponent implements ResourceComponent, OperationFacet {
static final String OPERATION_RESULT = "uninterrupted";
boolean naughty;
volatile boolean caughtInterruptedComponentInvocation;
ResourceContext resourceContext;
MockResourceComponent(boolean naughty) {
this.naughty = naughty;
}
@Override
public void start(ResourceContext resourceContext) throws Exception {
this.resourceContext = resourceContext;
}
@Override
public void stop() {
this.resourceContext = null;
}
@Override
public AvailabilityType getAvailability() {
if (this.naughty) {
throw new MockRuntimeException();
}
try {
Thread.sleep(1100L);
} catch (InterruptedException e) {
return AvailabilityType.DOWN;
}
return AvailabilityType.UP;
}
@Override
public String toString() {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return this.getClass().toString();
}
@Override
public OperationResult invokeOperation(String name, Configuration parameters) throws Exception {
caughtInterruptedComponentInvocation = false;
sleep(SECONDS.toMillis(5));
if (resourceContext.getComponentInvocationContext().isInterrupted()) {
caughtInterruptedComponentInvocation = true;
throw new InterruptedException();
}
return new OperationResult(OPERATION_RESULT);
}
private void sleep(long millis) {
// Do not return before 'millis' have elapsed, even if the thread is interrupted
long start = System.currentTimeMillis();
for (long stop = System.currentTimeMillis(); stop - start < millis; stop = System.currentTimeMillis()) {
try {
Thread.sleep(millis - (stop - start));
} catch (InterruptedException e) {
}
}
}
boolean caughtInterruptedComponentInvocation() {
return caughtInterruptedComponentInvocation;
}
}
class MockRuntimeException extends RuntimeException {
}
}