/* * RHQ Management Platform * Copyright (C) 2005-2014 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.drift; import static org.rhq.common.drift.FileEntry.addedFileEntry; import static org.rhq.common.drift.FileEntry.changedFileEntry; import static org.rhq.common.drift.FileEntry.removedFileEntry; import static org.rhq.core.domain.drift.DriftChangeSetCategory.COVERAGE; import static org.rhq.core.domain.drift.DriftChangeSetCategory.DRIFT; import static org.rhq.core.domain.drift.DriftComplianceStatus.OUT_OF_COMPLIANCE_NO_BASEDIR; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.util.List; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rhq.common.drift.ChangeSetWriter; import org.rhq.common.drift.Headers; import org.rhq.core.clientapi.agent.drift.DriftAgentService; import org.rhq.core.clientapi.server.drift.DriftServerService; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.drift.Drift; import org.rhq.core.domain.drift.DriftDefinition; import org.rhq.core.domain.drift.DriftFile; import org.rhq.core.domain.drift.DriftSnapshot; import org.rhq.core.domain.resource.Resource; import org.rhq.core.pc.ContainerService; import org.rhq.core.pc.PluginContainer; import org.rhq.core.pc.PluginContainerConfiguration; import org.rhq.core.pc.agent.AgentService; import org.rhq.core.pc.agent.AgentServiceStreamRemoter; import org.rhq.core.pc.inventory.InventoryManager; import org.rhq.core.pc.inventory.ResourceContainer; import org.rhq.core.util.file.FileUtil; import org.rhq.core.util.stream.StreamUtil; public class DriftManager extends AgentService implements DriftAgentService, DriftClient, ContainerService { private static final Log log = LogFactory.getLog(DriftManager.class); private final PluginContainerConfiguration pluginContainerConfiguration; private final File changeSetsDir; private final ScheduledThreadPoolExecutor driftThreadPool; private final ScheduleQueue schedulesQueue = new ScheduleQueueImpl(); /** * Not final for testing. */ private ChangeSetManager changeSetMgr; private final boolean initialized; private final InventoryManager inventoryManager; public DriftManager(PluginContainerConfiguration configuration, AgentServiceStreamRemoter streamRemoter, InventoryManager inventoryManager) { super(DriftAgentService.class, streamRemoter); this.inventoryManager = inventoryManager; pluginContainerConfiguration = configuration; changeSetsDir = new File(pluginContainerConfiguration.getDataDirectory(), "changesets"); long initStartTime = System.currentTimeMillis(); if (!changeSetsDir.isDirectory()) { boolean success = changeSetsDir.mkdir(); if (!success) { log.warn("Could not create change sets directory " + changeSetsDir); initialized = false; driftThreadPool = null; changeSetMgr = null; return; } } changeSetMgr = new ChangeSetManagerImpl(changeSetsDir); DriftDetector driftDetector = new DriftDetector(schedulesQueue, changeSetMgr, this); long startTime = System.currentTimeMillis(); initSchedules(inventoryManager.getPlatform(), inventoryManager); long endTime = System.currentTimeMillis(); if (log.isInfoEnabled()) { log.info("Finished initializing drift detection schedules in " + (endTime - startTime) + " ms"); } scanForContentToResend(); purgeDeletedDriftDefDirs(); driftThreadPool = new ScheduledThreadPoolExecutor(5); long initialDelay = pluginContainerConfiguration.getDriftDetectionInitialDelay(); long period = pluginContainerConfiguration.getDriftDetectionPeriod(); if (period > 0) { // note that drift detection is globally disabled if the detection period is 0 or less driftThreadPool.scheduleAtFixedRate(driftDetector, initialDelay, period, TimeUnit.SECONDS); } else { log.info("Drift detection has been globally disabled as per plugin container configuration"); } initialized = true; long initEndTime = System.currentTimeMillis(); if (log.isInfoEnabled()) { log.info("Finished initialization in " + (initEndTime - initStartTime) + " ms"); } } public boolean isInitialized() { return initialized; } private void initSchedules(Resource r, InventoryManager inventoryMgr) { if (r.getId() == 0) { log.debug("Will not reschedule drift detection for " + r + ". It is not sync'ed yet."); return; } ResourceContainer container = inventoryMgr.getResourceContainer(r.getId()); if (container == null) { log.debug("No resource container found for " + r + ". Unable to reschedule drift detection schedules."); return; } log.debug("Rescheduling drift detection for " + r); for (DriftDefinition d : container.getDriftDefinitions()) { try { syncWithServer(r, d); schedulesQueue.addSchedule(new DriftDetectionSchedule(r.getId(), d)); } catch (Throwable t) { // catch throwable, don't prevent agent startup just due to a bad definition log.error("Failed to sync with server for " + toString(r.getId(), d) + ". Drift detection will not be " + "scheduled.", t); } } for (Resource child : inventoryMgr.getContainerChildren(r, container)) { initSchedules(child, inventoryMgr); } } private void syncWithServer(Resource resource, DriftDefinition driftDefinition) throws IOException { Headers headers = createHeaders(resource.getId(), driftDefinition); if (!changeSetMgr.changeSetExists(resource.getId(), headers)) { log.info("No snapshot found for " + toString(resource.getId(), driftDefinition) + ". Downloading snapshot from server"); DriftServerService driftServer = pluginContainerConfiguration.getServerServices().getDriftServerService(); DriftSnapshot snapshot = driftServer.getCurrentSnapshot(driftDefinition.getId()); if (snapshot.getVersion() == -1) { // A version of -1 indicates that no change sets have been reported // for this definition. This can occur when a user creates a // drift definition while the agent is offline for example. At // this point we just return and allow the agent to generate the // initial snapshot file. if (log.isDebugEnabled()) { log.debug("The server does not have any change sets for " + toString(resource.getId(), driftDefinition) + ". An initial snapshot needs to be generated."); } return; } headers.setVersion(snapshot.getVersion()); log.info("Preparing to write snapshot at version " + snapshot.getVersion() + " to disk for " + toString(resource.getId(), driftDefinition)); File currentSnapshotFile = changeSetMgr .findChangeSet(resource.getId(), driftDefinition.getName(), COVERAGE); writeSnapshotToFile(snapshot, currentSnapshotFile, headers); if (driftDefinition.isPinned()) { log.debug(driftDefinition + " is pinned. Fetching pinned snapshot..."); // The pinned snapshot is always the initial change set and only the initial // change set. DriftSnapshot pinnedSnapshot = driftServer.getSnapshot(driftDefinition.getId(), 0, 0); Headers pinnedHeaders = createHeaders(resource.getId(), driftDefinition); File pinnedSnapshotFile = new File(currentSnapshotFile.getParent(), DriftDetector.FILE_SNAPSHOT_PINNED); log.info("Preparing to write pinned snapshot to disk for " + toString(resource.getId(), driftDefinition)); writeSnapshotToFile(pinnedSnapshot, pinnedSnapshotFile, pinnedHeaders); if (snapshot.getVersion() > 0) { // Drift was previously reported. We will fetch a snapshot of the // latest change set and write that to disk so that we avoid reporting // drift that has already been reported to the server. DriftSnapshot deltaSnapshot = driftServer.getSnapshot(driftDefinition.getId(), snapshot.getVersion(), snapshot.getVersion()); File deltaFile = new File(currentSnapshotFile.getParentFile(), DriftDetector.FILE_CHANGESET_DELTA); Headers deltaHeaders = createHeaders(resource.getId(), driftDefinition); deltaHeaders.setVersion(snapshot.getVersion()); deltaHeaders.setType(DRIFT); writeSnapshotToFile(deltaSnapshot, deltaFile, deltaHeaders); } } } } private void purgeDeletedDriftDefDirs() { log.info("Checking for deleted drift definitions"); for (File resourceDir : changeSetsDir.listFiles()) { int resourceId = Integer.parseInt(resourceDir.getName()); for (File defDir : resourceDir.listFiles()) { DriftDefinition driftDef = new DriftDefinition(new Configuration()); driftDef.setName(defDir.getName()); if (!schedulesQueue.contains(resourceId, driftDef)) { log.info("Detected deleted drift definition, DriftDefinition[name: " + driftDef.getName() + ", resourceId: " + resourceId + "]"); log.info("Deleting drift definition directory " + defDir.getPath()); FileUtil.purge(defDir, true); } } } } /** * Scans the changesets directory for any change set content zip files. This method * assumes that any content zip files found have not been received or persisted by the * server. Each content zip file is resent to the server. */ public void scanForContentToResend() { log.info("Scanning for change set content to resend..."); File[] files = changeSetsDir.listFiles(); if (files == null) { return; } for (File resourceDir : files) { for (File defDir : resourceDir.listFiles()) { for (File contentZipFile : defDir.listFiles(new ZipFileNameFilter("content_"))) { if (log.isDebugEnabled()) { log.debug("Resending " + contentZipFile.getPath()); } sendChangeSetContentToServer(Integer.parseInt(resourceDir.getName()), defDir.getName(), contentZipFile); } } } } /** * This method is provided as a test hook. * * @param changeSetMgr */ void setChangeSetMgr(ChangeSetManager changeSetMgr) { this.changeSetMgr = changeSetMgr; } /** * This method is provided as a test hook. * @return The schedule queue */ public ScheduleQueue getSchedulesQueue() { return schedulesQueue; } @Override public void shutdown() { if (driftThreadPool != null) { // TODO (ips, 04/30/12): Is it safe to pass true here to interrupt executing threads? PluginContainer.shutdownExecutorService(driftThreadPool, false); } schedulesQueue.clear(); changeSetMgr = null; } @Override public void sendChangeSetToServer(DriftDetectionSummary detectionSummary) { int resourceId = detectionSummary.getSchedule().getResourceId(); DriftDefinition driftDefinition = detectionSummary.getSchedule().getDriftDefinition(); if (!schedulesQueue.contains(resourceId, driftDefinition)) { return; } File changeSetFile; if (detectionSummary.getType() == COVERAGE) { changeSetFile = detectionSummary.getNewSnapshot(); } else { changeSetFile = detectionSummary.getDriftChangeSet(); } if (changeSetFile == null) { log.warn("changeset[resourceId: " + resourceId + ", driftDefinition: " + driftDefinition.getName() + "] was not found. Cancelling request to send change set " + "to server"); return; } DriftServerService driftServer = pluginContainerConfiguration.getServerServices().getDriftServerService(); String fileName = "changeset_" + System.currentTimeMillis() + ".zip"; final File zipFile = new File(changeSetFile.getParentFile(), fileName); try { ZipOutputStream stream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile))); FileInputStream fis = new FileInputStream(changeSetFile); stream.putNextEntry(new ZipEntry(changeSetFile.getName())); StreamUtil.copy(fis, stream, true); } catch (IOException e) { zipFile.delete(); throw new DriftDetectionException("Failed to create change set zip file " + zipFile.getPath(), e); // } finally { // try { // if (stream != null) { // stream.close(); // } // } catch (IOException e) { // log.warn("An error occurred while trying to close change set zip file output stream", e); // } } try { driftServer.sendChangesetZip(resourceId, zipFile.length(), remoteInputStream(new BufferedInputStream( new FileInputStream(zipFile)))); } catch (IOException e) { throw new DriftDetectionException("Failed to set change set for " + toString(resourceId, driftDefinition) + " to server"); } catch (RuntimeException e) { throw new DriftDetectionException("Failed to set change set for " + toString(resourceId, driftDefinition) + " to server"); } } @Override public void sendChangeSetContentToServer(int resourceId, String driftDefName, File contentZipFile) { try { int startIndex = "content_".length(); int endIndex = contentZipFile.getName().indexOf("."); String token = contentZipFile.getName().substring(startIndex, endIndex); DriftServerService driftServer = pluginContainerConfiguration.getServerServices().getDriftServerService(); driftServer.sendFilesZip(resourceId, driftDefName, token, contentZipFile.length(), remoteInputStream(new BufferedInputStream(new FileInputStream(contentZipFile)))); } catch (FileNotFoundException e) { log.error("An error occurred while trying to send change set content zip file " + contentZipFile.getPath() + " to server.", e); } } @Override public void repeatChangeSet(int resourceId, String driftDefName, int version) { DriftServerService driftServer = pluginContainerConfiguration.getServerServices().getDriftServerService(); driftServer.repeatChangeSet(resourceId, driftDefName, version); } @Override public void detectDrift(int resourceId, DriftDefinition driftDefinition) { if (log.isInfoEnabled()) { log.info("Received request to schedule drift detection immediately for [resourceId: " + resourceId + ", driftDefinitionId: " + driftDefinition.getId() + ", driftDefinitionName: " + driftDefinition.getName() + "]"); } DriftDetectionSchedule schedule = schedulesQueue.remove(resourceId, driftDefinition); if (schedule == null) { log.warn("No schedule found in the queue for [resourceId: " + resourceId + ", driftDefinitionId: " + driftDefinition.getId() + ", driftDefinitionName: " + driftDefinition.getName() + "]. No " + " work will be scheduled."); return; } log.debug("Resetting " + schedule + " for immediate detection."); schedule.resetSchedule(); boolean queueUpdated = schedulesQueue.addSchedule(schedule); if (queueUpdated) { if (log.isDebugEnabled()) { log.debug(schedule + " has been added to " + schedulesQueue + " for immediate detection."); } } else { log.warn("Failed to add " + schedule + " to " + schedulesQueue + " for immediate detection."); } } @Override public void scheduleDriftDetection(int resourceId, DriftDefinition driftDefinition) { DriftDetectionSchedule schedule = new DriftDetectionSchedule(resourceId, driftDefinition); if (log.isInfoEnabled()) { log.info("Scheduling drift detection for " + schedule); } boolean added = schedulesQueue.addSchedule(schedule); if (added) { if (log.isDebugEnabled()) { log.debug(schedule + " has been added to " + schedulesQueue); } ResourceContainer container = inventoryManager.getResourceContainer(resourceId); if (container != null) { container.addDriftDefinition(driftDefinition); } } else { log.warn("Failed to add " + schedule + " to " + schedulesQueue); } } @Override public boolean requestDriftFiles(int resourceId, Headers headers, List<? extends DriftFile> driftFiles) { if (log.isInfoEnabled()) { log.info("Server is requesting files for [resourceId: " + resourceId + ", driftDefinitionId: " + headers.getDriftDefinitionId() + ", driftDefinitionName: " + headers.getDriftDefinitionName() + "]"); } DriftFilesSender sender = new DriftFilesSender(); sender.setResourceId(resourceId); sender.setDriftClient(this); sender.setDriftFiles(driftFiles); sender.setHeaders(headers); sender.setChangeSetManager(changeSetMgr); driftThreadPool.execute(sender); return true; } @Override public void unscheduleDriftDetection(final int resourceId, final DriftDefinition driftDefinition) { log.info("Received request to unschedule drift detection for [resourceId:" + resourceId + ", driftDefinitionId: " + driftDefinition.getId() + ", driftDefinitionName: " + driftDefinition.getName() + "]."); DriftDetectionSchedule schedule = schedulesQueue.removeAndExecute(resourceId, driftDefinition, new Runnable() { @Override public void run() { File resourceDir = new File(changeSetsDir, Integer.toString(resourceId)); File changeSetDir = new File(resourceDir, driftDefinition.getName()); FileUtil.purge(changeSetDir, true); log.debug("Removed change set directory " + changeSetDir.getAbsolutePath()); } }); if (schedule != null) { ResourceContainer container = inventoryManager.getResourceContainer(resourceId); if (container != null) { container.removeDriftDefinition(schedule.getDriftDefinition()); } } if (log.isDebugEnabled()) { log.debug("Removed " + schedule + " from the queue " + schedulesQueue); } } @Override public void updateDriftDetection(int resourceId, DriftDefinition driftDefinition) { log.info("Received request to update schedule for " + toString(resourceId, driftDefinition)); DriftDetectionSchedule updatedSchedule = schedulesQueue.update(resourceId, driftDefinition); if (updatedSchedule == null) { updatedSchedule = new DriftDetectionSchedule(resourceId, driftDefinition); if (log.isInfoEnabled()) { log.info("No matching schedule was found in the queue. This must be a request to add a new " + "schedule. Adding " + updatedSchedule + " to " + schedulesQueue); } boolean added = schedulesQueue.addSchedule(updatedSchedule); if (added) { if (log.isDebugEnabled()) { log.debug(updatedSchedule + " has been added to " + schedulesQueue); } } else { log.warn("Failed to add " + updatedSchedule + " to " + schedulesQueue); } } else { if (log.isDebugEnabled()) { log.debug(updatedSchedule + " has been updated and added back to " + schedulesQueue); } else if (log.isInfoEnabled()) { log.info(updatedSchedule + " has been updated."); } if (!updatedSchedule.getDriftDefinition().isPinned()) { unpinDefinition(updatedSchedule); } } InventoryManager inventoryMgr = PluginContainer.getInstance().getInventoryManager(); ResourceContainer container = inventoryMgr.getResourceContainer(resourceId); if (container != null) { container.addDriftDefinition(driftDefinition); } if(driftDefinition.getInterval() < pluginContainerConfiguration.getDriftDetectionPeriod()) { Resource resource = inventoryManager.getResourceContainer(resourceId).getResource(); inventoryManager.handleInvalidPluginConfigurationResourceError(resource, new Throwable("Drift interval was set to " + driftDefinition.getInterval() + "s, which is lower than agent's minimum value of " + pluginContainerConfiguration.getDriftDetectionPeriod() + "s." + " Change the interval or agent's configuration value of rhq.agent.plugins.drift-detection.period-secs")); } } private void unpinDefinition(final DriftDetectionSchedule schedule) { if (log.isDebugEnabled()) { log.debug("Unpinning definition for " + toString(schedule.getResourceId(), schedule.getDriftDefinition())); } schedulesQueue.removeAndExecute(schedule.getResourceId(), schedule.getDriftDefinition(), new Runnable() { @Override public void run() { File currentSnapshot = changeSetMgr.findChangeSet(schedule.getResourceId(), schedule .getDriftDefinition().getName(), COVERAGE); File pinnedSnapshot = new File(currentSnapshot.getParentFile(), DriftDetector.FILE_SNAPSHOT_PINNED); pinnedSnapshot.delete(); if (log.isDebugEnabled()) { log.debug("Deleted pinned snapshot file " + pinnedSnapshot.getPath()); } schedulesQueue.addSchedule(schedule); } }); } @Override public void updateDriftDetection(int resourceId, DriftDefinition driftDef, DriftSnapshot driftSnapshot) { File currentSnapshot = changeSetMgr.findChangeSet(resourceId, driftDef.getName(), COVERAGE); File pinnedSnapshot = new File(currentSnapshot.getParentFile(), DriftDetector.FILE_SNAPSHOT_PINNED); Headers headers = createHeaders(resourceId, driftDef); try { writeSnapshotToFile(driftSnapshot, currentSnapshot, headers); } catch (IOException e) { log.error("An error occurred while writing snapshot file [" + currentSnapshot.getPath() + "] to disk", e); // TODO do we need to report the error to the server? currentSnapshot.delete(); return; } try { StreamUtil.copy(new BufferedInputStream(new FileInputStream(currentSnapshot)), new BufferedOutputStream( new FileOutputStream(pinnedSnapshot)), true); } catch (IOException e) { log.error("An error occurred while writing snapshot file [" + pinnedSnapshot.getPath() + "] to disk", e); currentSnapshot.delete(); pinnedSnapshot.delete(); return; } updateDriftDetection(resourceId, driftDef); } @Override public void reportMissingBaseDir(int resourceId, DriftDefinition driftDefinition) { if (log.isDebugEnabled()) { log.debug("Reporting to server missing base directory for " + toString(resourceId, driftDefinition)); } DriftServerService driftServer = pluginContainerConfiguration.getServerServices().getDriftServerService(); driftServer.updateCompliance(resourceId, driftDefinition.getName(), OUT_OF_COMPLIANCE_NO_BASEDIR); } @Override public void pinSnapshot(final int resourceId, final String defName, final DriftSnapshot snapshot) { // When we pin a snapshot for an existing drift definition, we reset. We delete the // detection schedule and create a new schedule. We reset because the pinned // snapshot is always set to version zero. if (log.isInfoEnabled()) { log.info("Pinning snapshot for " + toString(resourceId, defName)); } final DriftDetectionSchedule schedule = schedulesQueue.find(resourceId, defName); if (schedule == null) { if (log.isDebugEnabled()) { log.debug("Unable to pin snapshot for " + toString(resourceId, defName) + " - no detection schedule " + "found."); } return; } DriftDefinition driftDef = schedule.getDriftDefinition(); driftDef.setPinned(true); unscheduleDriftDetection(resourceId, driftDef); updateDriftDetection(resourceId, driftDef, snapshot); } @Override public void ackChangeSet(int resourceId, String defName) { log.info("Received server change set ack for [resourceId: " + resourceId + ", driftDefinition:" + defName + "]"); File resourceDir = new File(changeSetsDir, Integer.toString(resourceId)); File changeSetDir = new File(resourceDir, defName); if (!changeSetDir.exists()) { log.warn("Cannot complete acknowledgement. Change set directory " + changeSetDir.getPath() + " does not exist."); return; } try { File snapshot = changeSetMgr.findChangeSet(resourceId, defName, COVERAGE); if (null == snapshot) { log.warn("Cannot complete acknowledgement. Could not find coverage changeset for [" + resourceId + "," + defName + "]."); return; } File previousSnapshot = new File(snapshot.getParentFile(), snapshot.getName() + ".previous"); previousSnapshot.delete(); } finally { deleteZipFiles(changeSetDir, "changeset_"); } } @Override public void ackChangeSetContent(int resourceId, String driftDefName, String token) { log.info("Received server change set content ack for [resourceId: " + resourceId + ", driftDefinitionName: " + driftDefName + "]"); File resourceDir = new File(changeSetsDir, Integer.toString(resourceId)); File changeSetDir = new File(resourceDir, driftDefName); if (!changeSetDir.exists()) { log.warn("Cannot complete acknowledgement. Change set directory " + changeSetDir.getPath() + " does not exist."); return; } deleteZipFiles(changeSetDir, "content_" + token); } private void deleteZipFiles(File dir, final String prefix) { for (File file : dir.listFiles(new ZipFileNameFilter(prefix))) { file.delete(); } } /** * Given a drift definition, this examines the def and its associated resource to determine where exactly * the base directory is that should be monitoried. * * @param resourceId The id of the resource to which the def belongs * @param driftDefinition describes what is to be monitored for drift * * @return absolute directory location where the drift def base directory is referring */ @Override public File getAbsoluteBaseDirectory(int resourceId, DriftDefinition driftDefinition) { // get the resource entity stored in our local inventory ResourceContainer container = inventoryManager.getResourceContainer(resourceId); if (container == null) { log.error("Cannot determine base directory for " + driftDefinition + ". No resource container found " + "for resource id " + resourceId + ". You may want to restart the agent with the -u option so that " + "the agent's local inventory is synchronized with and consistent with the server's inventory."); throw new IllegalArgumentException("Cannot determine base directory for " + driftDefinition + ". No resource container found for resource id " + resourceId); } Resource resource = container.getResource(); // find out the type of base location that is specified by the drift def DriftDefinition.BaseDirectory baseDir = driftDefinition.getBasedir(); if (baseDir == null) { throw new IllegalArgumentException("Base directory is null for drift definition [" + driftDefinition.getName() + "]"); } // based on the type of base location, determine the root base directory String baseDirValueName = baseDir.getValueName(); // the name we look up in the given context String baseLocation; switch (baseDir.getValueContext()) { case fileSystem: { baseLocation = baseDirValueName; // the value name IS the absolute directory name if (baseLocation == null || baseLocation.trim().length() == 0) { baseLocation = File.separator; // paranoia, if not specified, assume the top root directory } break; } case pluginConfiguration: { baseLocation = resource.getPluginConfiguration().getSimpleValue(baseDirValueName, null); if (baseLocation == null) { throw new IllegalArgumentException("Cannot determine the base directory - " + "there is no plugin configuration setting for [" + baseDirValueName + "]"); } break; } case resourceConfiguration: { baseLocation = InventoryManager.getResourceConfiguration(resource).getSimpleValue( baseDirValueName, null); if (baseLocation == null) { throw new IllegalArgumentException("Cannot determine the base directory - " + "there is no resource configuration setting for [" + baseDirValueName + "]"); } break; } case measurementTrait: { baseLocation = getTraitValue(container, baseDirValueName); if (baseLocation == null) { throw new IllegalArgumentException("Cannot obtain trait [" + baseDirValueName + "] for resource [" + resource.getName() + "]"); } break; } default: { throw new IllegalArgumentException("Unknown location context: " + baseDir.getValueContext()); } } File destDir = new File(baseLocation); if (!destDir.isAbsolute()) { throw new IllegalArgumentException("The base location path specified by [" + baseDirValueName + "] in the context [" + baseDir.getValueContext() + "] did not resolve to an absolute path [" + destDir.getPath() + "] so there is no way to know what directory to monitor for drift"); } return destDir; } private String getTraitValue(ResourceContainer container, String traitName) { return inventoryManager.getMeasurementManager().getTraitValue(container, traitName); } private void writeSnapshotToFile(DriftSnapshot snapshot, File file, Headers headers) throws IOException { ChangeSetWriter writer = changeSetMgr.getChangeSetWriter(file, headers); try { for (Drift<?, ?> drift : snapshot.getDriftInstances()) { switch (drift.getCategory()) { case FILE_ADDED: writer.write(addedFileEntry(drift.getPath(), drift.getNewDriftFile().getHashId(), -1L, -1L)); break; case FILE_CHANGED: writer.write(changedFileEntry(drift.getPath(), drift.getOldDriftFile().getHashId(), drift .getNewDriftFile().getHashId(), -1L, -1L)); break; default: // FILE_REMOVED writer.write(removedFileEntry(drift.getPath(), drift.getOldDriftFile().getHashId())); } } } finally { writer.close(); } } private String toString(int resourceId, DriftDefinition d) { return "DriftDefinition[id: " + d.getId() + ", name: " + d.getName() + ", resourceId: " + resourceId + "]"; } private String toString(int resourceId, String defName) { return "[resourceId: " + resourceId + ", driftDefintionName: " + defName + "]"; } private Headers createHeaders(int resourceId, DriftDefinition driftDef) { Headers headers = new Headers(); headers.setResourceId(resourceId); headers.setDriftDefinitionId(driftDef.getId()); headers.setType(COVERAGE); headers.setDriftDefinitionName(driftDef.getName()); headers.setBasedir(getAbsoluteBaseDirectory(resourceId, driftDef).getAbsolutePath()); return headers; } private static class ZipFileNameFilter implements FilenameFilter { private final String prefix; public ZipFileNameFilter(String prefix) { this.prefix = prefix; } @Override public boolean accept(File dir, String name) { return name.startsWith(prefix) && name.endsWith(".zip"); } } public InventoryManager getInventoryManager() { return inventoryManager; } }