package org.rhq.enterprise.server.resource.metadata; import static org.rhq.core.clientapi.shared.PluginDescriptorUtil.loadPluginDescriptor; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.URL; import java.sql.Connection; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import org.apache.commons.beanutils.MethodUtils; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.lang.WordUtils; import org.apache.maven.artifact.versioning.ComparableVersion; import org.dbunit.database.DatabaseConfig; import org.dbunit.database.DatabaseConnection; import org.dbunit.database.IDatabaseConnection; import org.dbunit.dataset.DataSetException; import org.dbunit.dataset.IDataSet; import org.dbunit.dataset.datatype.IDataTypeFactory; import org.dbunit.dataset.xml.FlatXmlDataSet; import org.dbunit.dataset.xml.FlatXmlProducer; import org.dbunit.operation.DatabaseOperation; import org.xml.sax.InputSource; import org.rhq.core.clientapi.descriptor.plugin.PluginDescriptor; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.cloud.Server; import org.rhq.core.domain.criteria.ResourceTypeCriteria; import org.rhq.core.domain.criteria.ServerCriteria; import org.rhq.core.domain.plugin.Plugin; import org.rhq.core.domain.resource.ResourceType; import org.rhq.core.util.MessageDigestGenerator; import org.rhq.core.util.stream.StreamUtil; import org.rhq.enterprise.server.auth.SubjectManagerLocal; import org.rhq.enterprise.server.bundle.TestBundlePluginComponent; import org.rhq.enterprise.server.bundle.TestBundleServerPluginService; import org.rhq.enterprise.server.resource.ResourceTypeManagerLocal; import org.rhq.enterprise.server.scheduler.jobs.PurgePluginsJob; import org.rhq.enterprise.server.scheduler.jobs.PurgeResourceTypesJob; import org.rhq.enterprise.server.test.AbstractEJB3Test; import org.rhq.enterprise.server.util.LookupUtil; public class MetadataBeanTest extends AbstractEJB3Test { private final String objectFileName = "pluginIds.obj"; private Set<Integer> pluginIds; /** * <pre>IMPORTANT NOTE FOR SUBCLASS IMPLEMENTORS * Arquillian (1.0.2) executes @AfterXXX after each test. The work below would normally be done in * an AfterClass method, but instead we'll use a "special" test that performs last for each subclass, to * perform this cleanup code. * </pre> * Need to delete rows from RHQ_PLUGINS because subsequent tests in server/jar would otherwise fail. Some tests look * at what plugins are in the database, and then look for corresponding plugin files on the file system. MetadataTest * however removes the generated plugin files during each test run. * * Note that using this for cleanup is discouraged in favor or beforeMethod and afterMethod overrides. Remember * that tests and test classes can execute in any order. This only guarantees clean up at some point, other tests * [in other test classes] may encounter data that is left for cleanup by this mechanism. */ protected void afterClassWork() throws Exception { PluginManagerLocal pluginMgr = LookupUtil.getPluginManager(); Subject overlord = LookupUtil.getSubjectManager().getOverlord(); List<Integer> doomedPlugins = new ArrayList<Integer>(pluginIds); pluginMgr.deletePlugins(overlord, doomedPlugins); //the following 3 lines ensure we truly delete the above plugins //from the database ackDeletedPlugins(); new PurgeResourceTypesJob().executeJobCode(null); new PurgePluginsJob().executeJobCode(null); pluginIds.clear(); deleteObjects(objectFileName); } @Override protected void beforeMethod() throws Exception { setupDB(); TestBundleServerPluginService bundleService = new TestBundleServerPluginService(getTempDir(), new TestBundlePluginComponent()); prepareCustomServerPluginService(bundleService); bundleService.startMasterPluginContainerWithoutSchedulingJobs(); prepareScheduler(); preparePluginScannerService(); try { List<Object> objects = readObjects(objectFileName, 1); pluginIds = (Set<Integer>) objects.get(0); } catch (Throwable t) { pluginIds = new HashSet<Integer>(); } } /** * Need to delete rows from RHQ_PLUGINS because subsequent tests in server/jar would otherwise fail. Some tests look * at what plugins are in the database, and then look for corresponding plugin files on the file system. MetadataTest * however removes the generated plugin files during each test run. */ @Override protected void afterMethod() throws Exception { if (!pluginIds.isEmpty()) { writeObjects(objectFileName, pluginIds); } unpreparePluginScannerService(); unprepareServerPluginService(); unprepareScheduler(); } protected void setupDB() throws Exception { Connection connection = null; try { connection = getConnection(); DatabaseConnection dbunitConnection = new DatabaseConnection(connection); setDbType(dbunitConnection); // note - this info should already be in the db as part of dbsetup, but just in case // perform the refresh. Do not DELETE this data set as it may be assumed in other tests. DatabaseOperation.REFRESH.execute(dbunitConnection, getDataSet()); } finally { if (connection != null) { connection.close(); } } } private void setDbType(IDatabaseConnection connection) throws Exception { DatabaseConfig config = connection.getConfig(); String name = connection.getConnection().getMetaData().getDatabaseProductName().toLowerCase(); int major = connection.getConnection().getMetaData().getDatabaseMajorVersion(); IDataTypeFactory type = null; if (name.contains("postgres")) { type = new org.dbunit.ext.postgresql.PostgresqlDataTypeFactory(); } else if (name.contains("oracle")) { if (major >= 10) { type = new org.dbunit.ext.oracle.Oracle10DataTypeFactory(); } else { type = new org.dbunit.ext.oracle.OracleDataTypeFactory(); } } if (type != null) { config.setProperty("http://www.dbunit.org/properties/datatypeFactory", type); } } private IDataSet getDataSet() throws DataSetException { FlatXmlProducer xmlProducer = new FlatXmlProducer(new InputSource(getClass().getResourceAsStream( getDataSetFile()))); xmlProducer.setColumnSensing(true); return new FlatXmlDataSet(xmlProducer); } protected String getDataSetFile() { return "MetadataTest.xml"; } protected void createPlugin(String pluginFileName, String version, String descriptorFileName) throws Exception { URL descriptorURL = getDescriptorURL(descriptorFileName); PluginDescriptor pluginDescriptor = loadPluginDescriptor(descriptorURL); String pluginFilePath = getPluginScannerService().getAgentPluginDir() + "/" + pluginFileName + ".jar"; Plugin plugin = new Plugin(pluginDescriptor.getName(), pluginFilePath); plugin.setDisplayName(pluginDescriptor.getName()); plugin.setEnabled(true); plugin.setDescription(pluginDescriptor.getDescription()); plugin.setAmpsVersion(getAmpsVersion(pluginDescriptor)); plugin.setVersion(pluginDescriptor.getVersion()); plugin.setMD5(MessageDigestGenerator.getDigestString(descriptorURL)); PluginManagerLocal pluginMgr = LookupUtil.getPluginManager(); pluginMgr.registerPlugin(plugin, pluginDescriptor, null, true); pluginIds.add(plugin.getId()); } private URL getDescriptorURL(String descriptor) { String dir = getClass().getSimpleName(); return getClass().getResource(dir + "/" + descriptor); } String getAmpsVersion(PluginDescriptor pluginDescriptor) { if (pluginDescriptor.getAmpsVersion() == null) { return "2.0"; } ComparableVersion version = new ComparableVersion(pluginDescriptor.getAmpsVersion()); ComparableVersion version2 = new ComparableVersion("2.0"); if (version.compareTo(version2) <= 0) { return "2.0"; } return pluginDescriptor.getAmpsVersion(); } protected ResourceType assertResourceTypeAssociationEquals(String resourceTypeName, String plugin, String propertyName, List<String> expected) throws Exception { SubjectManagerLocal subjectMgr = LookupUtil.getSubjectManager(); ResourceTypeManagerLocal resourceTypeMgr = LookupUtil.getResourceTypeManager(); String fetch = "fetch" + WordUtils.capitalize(propertyName); ResourceTypeCriteria criteria = new ResourceTypeCriteria(); criteria.addFilterName(resourceTypeName); criteria.addFilterPluginName(plugin); criteria.setStrict(true); criteria.fetchBundleConfiguration(true); criteria.fetchDriftDefinitionTemplates(true); MethodUtils.invokeMethod(criteria, fetch, true); List<ResourceType> resourceTypes = resourceTypeMgr.findResourceTypesByCriteria(subjectMgr.getOverlord(), criteria); assertEquals("too many types!", 1, resourceTypes.size()); ResourceType resourceType = resourceTypes.get(0); Set<String> expectedSet = new HashSet<String>(expected); List<String> missing = new ArrayList<String>(); List<String> unexpected = new ArrayList<String>(); for (String expectedProperty : expectedSet) { if (!contains(resourceType, propertyName, expectedProperty)) { missing.add(expectedProperty); } } Collection<?> actualPropertyValues = (Collection<?>) PropertyUtils.getProperty(resourceType, propertyName); for (Object actualPropertyValue : actualPropertyValues) { String actualName = (String) PropertyUtils.getProperty(actualPropertyValue, "name"); if (!expectedSet.contains(actualName)) { unexpected.add(actualName); } } String errors = ""; if (!missing.isEmpty()) { errors = "Failed to find the following " + propertyName + "(s) for type " + resourceTypeName + ": " + missing; } if (unexpected.size() > 0) { errors += "\nFound unexpected " + propertyName + "(s) for type " + resourceTypeName + ": " + unexpected; } assert errors.isEmpty() : errors; return resourceType; } private boolean contains(ResourceType type, String propertyName, String expected) throws Exception { Collection<?> actualPropertyValues = (Collection<?>) PropertyUtils.getProperty(type, propertyName); for (Object actualPropertyValue : actualPropertyValues) { String actualName = (String) PropertyUtils.getProperty(actualPropertyValue, "name"); if (actualName.equals(expected)) { return true; } } return false; } /** * This actually creates a .jar file on the file system but doesn't register it. * * @param jarName the name to be given to the new jar file * @param descriptorXmlFilename where the descriptor XML can be found on the test classloader * @return the location of the new jar file * @throws Exception */ protected File createPluginJarFile(String jarName, String descriptorXmlFilename) throws Exception { FileOutputStream stream = null; JarOutputStream out = null; InputStream in = null; try { String pluginDirPath = getPluginScannerService().getAgentPluginDir(); File pluginDir = new File(pluginDirPath); pluginDir.mkdirs(); File jarFile = new File(pluginDir, jarName); jarFile.delete(); // in case some older file is hanging around, get rid of it stream = new FileOutputStream(jarFile); out = new JarOutputStream(stream); // Add archive entry for the descriptor JarEntry jarAdd = new JarEntry("META-INF/rhq-plugin.xml"); jarAdd.setTime(System.currentTimeMillis()); out.putNextEntry(jarAdd); // Write the descriptor - note that we assume the xml file is in the test classloader URL descriptorURL = getDescriptorURL(descriptorXmlFilename); in = descriptorURL.openStream(); StreamUtil.copy(in, out, false); return jarFile; } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } if (stream != null) { stream.close(); } } } /** * Tests can use this to let us know that a plugin has been deployed and needs to * be cleaned up/removed at the end of the test. * * @param pluginName */ protected void pluginDeployed(String pluginName) { PluginManagerLocal pluginMgr = LookupUtil.getPluginManager(); Plugin plugin = pluginMgr.getPlugin(pluginName); if (plugin != null) { this.pluginIds.add(plugin.getId()); } } /** * Use this to ignore a plugin's resource type. Useful for making sure metadata * updates work even for types that are ignored. * * @param typeName type to ignore * @param pluginName the plugin where the type is defined */ protected void ignoreType(String typeName, String pluginName) { SubjectManagerLocal subjectMgr = LookupUtil.getSubjectManager(); ResourceTypeManagerLocal typeMgr = LookupUtil.getResourceTypeManager(); ResourceType rt = typeMgr.getResourceTypeByNameAndPlugin(typeName, pluginName); if (rt == null) { fail("Should have had a resource type named [" + typeName + "] from plugin [" + pluginName + "]"); } typeMgr.setResourceTypeIgnoreFlagAndUninventoryResources(subjectMgr.getOverlord(), rt.getId(), true); // sanity check - make sure the type really got ignored rt = typeMgr.getResourceTypeByNameAndPlugin(typeName, pluginName); if (!rt.isIgnored()) { fail("Should have ignored resource type [" + rt + "]"); } return; } }