package org.limewire.core.impl.inspections; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import org.limewire.logging.Log; import org.limewire.logging.LogFactory; import org.limewire.util.StringUtils; import org.limewire.io.InvalidDataException; import org.limewire.core.settings.ApplicationSettings; import org.limewire.inspection.Inspector; import org.limewire.inspection.InspectionException; /** * represents an inspections instruction - a response * sent from the lw server telling the client: * * Contains all inspection points to be collected * after the same delay and at the same intervals, is one * of a list of instructions sent from the server. * */ public class InspectionsSpec { private static final Log LOG = LogFactory.getLog(InspectionsSpec.class); /** * inspection points specified by name. */ private final List<String> inspectionPoints; /** * delay (seconds) before the inspections in this spec are first executed. */ private final long startDelay; /** * Delay (seconds) between executions of the inspections in this spec */ private final long interval; /** * A handle to the scheduled inspections so we can cancel * or reschedule. */ private ScheduledFuture scheduledInspections; /** * Creates based on parameters: inspection key list, * start delay, and interval between inspections executed. * * @param inspectionPoints inspection points specified by name. * @param startDelay delay (in seconds) before the inspections in this spec are first executed. * @param interval delay (in seconds) between executions of inspections in this spec. */ InspectionsSpec(List<String> inspectionPoints, long startDelay, long interval) { this.inspectionPoints = new ArrayList<String>(inspectionPoints); this.startDelay = startDelay; this.interval = interval; } /** * Creates given a bdecoded Object. The Object must be in * the following format, or else it will * throw an InvalidDataException. bdecoded must be a Map, * containing "startdelay" -> int, "interval" -> int, * "insp" -> list of Strings * * Map * "startdelay" -> integer * "interval" -> integer * "insp" -> list of inspection point strings * point1 (String), * point2 (String), * point3 (String), ... * * @param bdecoded BDecoded Object * @throws InvalidDataException if bdecoded Object has wrong format * (see above for proper format) */ InspectionsSpec(Object bdecoded) throws InvalidDataException { try { Map<?, ?> specMap = (Map<?, ?>) bdecoded; long startDelay = (Long) specMap.get("startdelay"); long interval = (Long) specMap.get("interval"); List<?> inspPointsEncoded = (List<?>) specMap.get("insp"); List<String> inspectionPoints = new ArrayList<String>(); for (Object inspPt : inspPointsEncoded) { inspectionPoints.add(StringUtils.getUTF8String((byte[]) inspPt)); } this.inspectionPoints = new ArrayList<String>(inspectionPoints); this.startDelay = startDelay; this.interval = interval; } catch (ClassCastException e) { throw new InvalidDataException("invalid inspections specification data", e); } } Map<String, Object> asBencodedMap() { // "startdelay" -> integer // "interval" -> integer // "insp" -> list of inspection point strings Map<String, Object> inspectionSpecEncoded = new HashMap<String, Object>(3); inspectionSpecEncoded.put("startdelay", startDelay); inspectionSpecEncoded.put("interval", interval); inspectionSpecEncoded.put("insp", inspectionPoints); return inspectionSpecEncoded; } long getInitialDelay() { return startDelay; } long getInterval() { return interval; } List<String> getInspectionPoints() { return Collections.unmodifiableList(inspectionPoints); } private InspectionDataContainer inspect(Inspector inspector, boolean collectUsageData) { InspectionDataContainer inspectionResults = new InspectionDataContainer(); // inspect individual inspection points for (String inspectionKey : inspectionPoints) { try { inspectionResults.addInspectionResult(inspectionKey, inspector.inspect(inspectionKey, collectUsageData)); } catch (InspectionException e) { LOG.error("Error performing inspection", e); Map<String, String> errorMap = new HashMap<String, String>(); errorMap.put("error", e.getMessage()); inspectionResults.addInspectionResult(inspectionKey, errorMap); } } return inspectionResults; } // todo: consider creating with InspectionsSpecFactory, injecting in Inspector, InspectionsCommunicator, ScheduledExecutorService synchronized void schedule(final InspectionsResultProcessor processor, final Inspector inspector, ScheduledExecutorService scheduler) { ensureCancelled(); Runnable doInspection = new InspectionsSpecProcessing(processor, inspector); if (interval == 0) { scheduledInspections = scheduler.schedule(doInspection, startDelay, TimeUnit.SECONDS); } else { scheduledInspections = scheduler.scheduleWithFixedDelay(doInspection, startDelay, interval, TimeUnit.SECONDS); } } synchronized void ensureCancelled() { // if necessary, cancel future representing scheduled inspections if (scheduledInspections != null) { this.scheduledInspections.cancel(false); this.scheduledInspections = null; } } private class InspectionsSpecProcessing implements Runnable { private final Inspector inspector; private final InspectionsResultProcessor processor; InspectionsSpecProcessing(InspectionsResultProcessor processor, Inspector inspector) { this.processor = processor; this.inspector = inspector; } @Override public void run() { // inspect and add all finished inspections to queue InspectionDataContainer inspResults = inspect(inspector, ApplicationSettings.ALLOW_ANONYMOUS_STATISTICS_GATHERING.get()); try { processor.inspectionsPerformed(inspResults); } catch (InspectionProcessingException e) { ensureCancelled(); } } } }