/*
* RHQ Management Platform
* Copyright (C) 2005-2011 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.plugins.apache.augeas;
import static org.testng.Assert.assertEquals;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jmock.Expectations;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import org.jboss.byteman.contrib.bmunit.BMNGRunner;
import org.jboss.byteman.contrib.bmunit.BMRule;
import org.jboss.byteman.contrib.bmunit.BMRules;
import org.rhq.core.clientapi.agent.PluginContainerException;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pc.PluginContainer;
import org.rhq.core.pc.ServerServices;
import org.rhq.core.pc.configuration.ConfigurationManager;
import org.rhq.core.pc.inventory.InventoryManager;
import org.rhq.core.pc.inventory.ResourceContainer;
import org.rhq.core.pc.upgrade.FakeServerInventory;
import org.rhq.plugins.apache.ApacheServerComponent;
import org.rhq.plugins.apache.PluginLocation;
import org.rhq.plugins.apache.setup.ApacheTestConfiguration;
import org.rhq.plugins.apache.setup.ApacheTestSetup;
import org.rhq.plugins.apache.upgrade.UpgradeTestBase;
import org.rhq.plugins.apache.util.ResourceTypes;
import org.rhq.test.pc.PluginContainerSetup;
import org.rhq.test.pc.PluginContainerTest;
/**
*
*
* @author Lukas Krejci
*/
@Listeners(PluginContainerTest.class)
@Test
public class AugeasReferenceLeakingTest extends BMNGRunner {
private ApacheTestSetup setup;
@BeforeMethod
public void clearCreateAndCloseTracker() {
CreateAndCloseTracker.clear();
}
@SuppressWarnings("unchecked")
@PluginContainerSetup(plugins = { PluginLocation.PLATFORM_PLUGIN, PluginLocation.AUGEAS_PLUGIN,
PluginLocation.APACHE_PLUGIN })
@BMRules(
rules = {
@BMRule(name = "increment reference count on Augeas init", targetClass = "net.augeas.Augeas",
targetMethod = "<init>(String, String, int)",
helper = "org.rhq.plugins.apache.augeas.CreateAndCloseTracker",
action = "recordCreate($0, formatStack())"),
@BMRule(name = "decrement reference count on Augeas close", targetClass = "net.augeas.Augeas",
targetMethod = "close()", helper = "org.rhq.plugins.apache.augeas.CreateAndCloseTracker",
action = "recordClose($0, formatStack())") })
@Parameters({ "apache2.install.dir", "apache2.exe.path",
"AugeasReferenceLeakingTest.configurationReadingInvocationCount" })
public void testReadingConfigurationsDoesNotLeakAugeasReferences(final String installDir, final String exePath,
int configurationReadingInvocationCount) throws Exception {
final FakeServerInventory fakeInventory = new FakeServerInventory();
PluginContainerTest.getCurrentMockContext().checking(new Expectations() {
{
ServerServices ss = PluginContainerTest.getCurrentPluginContainerConfiguration().getServerServices();
allowing(ss.getDiscoveryServerService()).mergeInventoryReport(with(any(InventoryReport.class)));
will(fakeInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
allowing(ss.getDiscoveryServerService()).getResources(with(any(Set.class)), with(any(boolean.class)));
will(fakeInventory.getResources());
allowing(ss.getDiscoveryServerService()).mergeAvailabilityReport(with(any(AvailabilityReport.class)));
allowing(ss.getDiscoveryServerService()).postProcessNewlyCommittedResources(with(any(Set.class)));
allowing(ss.getDiscoveryServerService()).setResourceEnablement(with(any(int.class)),
with(any(boolean.class)));
ignoring(ss.getBundleServerService());
ignoring(ss.getConfigurationServerService());
ignoring(ss.getContentServerService());
ignoring(ss.getCoreServerService());
ignoring(ss.getEventServerService());
ignoring(ss.getMeasurementServerService());
ignoring(ss.getOperationServerService());
ignoring(ss.getResourceFactoryServerService());
}
});
boolean apacheStarted = false;
try {
apacheStarted = startApache(installDir, exePath);
PluginContainerTest.startConfiguredPluginContainer();
configureApacheServerToUseAugeas();
PluginContainer pc = PluginContainer.getInstance();
Resource apacheServer = findApacheServerResource().getResource();
for (int i = 0; i < configurationReadingInvocationCount; ++i) {
checkApacheServerConfigurationRecursively(apacheServer, pc.getConfigurationManager());
Thread.sleep(10000);
}
//wait a couple of seconds for the loadConfig calls to finish
Thread.sleep(60000);
} finally {
if (apacheStarted) {
stopApache();
}
}
}
@AfterMethod
public void checkForLeaksAndMultiCloses() {
List<String> leaks = CreateAndCloseTracker.getCreateLocationsWithoutClose();
Map<String, List<String>> multiCloses = CreateAndCloseTracker.getMultiplyClosingLocations();
assertEquals(leaks.size(), 0,
"There were Augeas instances without a close() call on them created at the following locations: " + leaks);
assertEquals(
multiCloses.size(),
0,
"Each key in the map is a location where Augeas was created and the values are the lists of locations where that instance was closed: "
+ multiCloses);
}
private boolean startApache(final String installDir, final String exePath) throws Exception {
ApacheTestConfiguration apacheConfig = new ApacheTestConfiguration() {
{
serverRoot = installDir;
binPath = exePath;
configurationName = "augeas-leak-test-config";
apacheConfigurationFiles = new String[] { "/augeas-leak-test-config/httpd.conf", "/snmpd.conf" };
inventoryFile = null;
}
};
setup = new ApacheTestSetup(this.getClass().getSimpleName()
+ "#testReadingConfigurationsDoesNotLeakAugeasReferences", apacheConfig.configurationName,
PluginContainerTest.getCurrentMockContext(),
new ResourceTypes(PluginLocation.APACHE_PLUGIN));
Resource platform = UpgradeTestBase.discoverPlatform();
setup.withInventoryFrom(apacheConfig.inventoryFile).withPlatformResource(platform)
.withDefaultOverrides(apacheConfig.defaultOverrides).withApacheSetup()
.withConfigurationFiles(apacheConfig.apacheConfigurationFiles).withServerRoot(apacheConfig.serverRoot)
.withExePath(apacheConfig.binPath);
setup.setup();
return true;
}
private void stopApache() throws Exception {
if (setup != null) {
setup.withApacheSetup().stopApache();
setup = null;
}
}
private void checkApacheServerConfigurationRecursively(Resource resource, ConfigurationManager cm)
throws PluginContainerException {
if (resource.getResourceType().getResourceConfigurationDefinition() != null) {
cm.loadResourceConfiguration(resource.getId());
}
for (Resource child : resource.getChildResources()) {
checkApacheServerConfigurationRecursively(child, cm);
}
}
private void configureApacheServerToUseAugeas() throws Exception {
ResourceTypes resourceTypes = new ResourceTypes(PluginLocation.APACHE_PLUGIN);
InventoryManager im = PluginContainer.getInstance().getInventoryManager();
ResourceContainer apacheServer = findApacheServerResource();
Configuration config = apacheServer.getResourceContext().getPluginConfiguration();
config.getSimple("augeasEnabled").setValue("yes");
im.updatePluginConfiguration(apacheServer.getResource().getId(), config);
//and run discovery so that the new resources can go into inventory
im.executeServiceScanImmediately();
}
private ResourceContainer findApacheServerResource() throws Exception {
InventoryManager im = PluginContainer.getInstance().getInventoryManager();
ResourceTypes resourceTypes = new ResourceTypes(PluginLocation.APACHE_PLUGIN);
ResourceType apacheServerResourceType = resourceTypes.findByName("Apache HTTP Server");
return findApacheServerResource(im, apacheServerResourceType, im.getPlatform());
}
private ResourceContainer findApacheServerResource(InventoryManager im, ResourceType rt, Resource root) {
if (root.getResourceType().equals(rt)
&& root.getPluginConfiguration().getSimpleValue(ApacheServerComponent.PLUGIN_CONFIG_PROP_SERVER_ROOT)
.equals(setup.getDeploymentConfig().serverRoot)) {
return im.getResourceContainer(root);
}
for (Resource child : root.getChildResources()) {
ResourceContainer rc = findApacheServerResource(im, rt, child);
if (rc != null) {
return rc;
}
}
return null;
}
}