diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java b/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java index b23d48dc31d9c1907cad1b3b198dd9260c3ff382..4f2e65fa5ab83b77867d1732940ab39627661dab 100644 --- a/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java +++ b/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java @@ -94,7 +94,8 @@ public final class DirectoryScanningTimerTask extends TimerTask * @param filter The file filter that picks the entries to handle. * @param handler The handler that is used for treating the matching paths. */ - public DirectoryScanningTimerTask(File sourceDirectory, FileFilter filter, IPathHandler handler) + public DirectoryScanningTimerTask(final File sourceDirectory, final FileFilter filter, + final IPathHandler handler) { this(sourceDirectory, filter, handler, 0); } @@ -109,13 +110,19 @@ public final class DirectoryScanningTimerTask extends TimerTask * to occur before the next error is logged (can be used to suppress error when the * directory is on a remote share and the server is flaky sometimes) */ - public DirectoryScanningTimerTask(File sourceDirectory, FileFilter filter, - IPathHandler handler, int ignoredErrorCount) + public DirectoryScanningTimerTask(final File sourceDirectory, final FileFilter filter, + final IPathHandler handler, final int ignoredErrorCount) { this(asScannedStore(sourceDirectory, filter), sourceDirectory, asScanningHandler( sourceDirectory, handler), ignoredErrorCount); } + public DirectoryScanningTimerTask(final File sourceDirectory, final FileFilter filter, + final IStoreHandler handler) + { + this(asScannedStore(sourceDirectory, filter), sourceDirectory, handler, 0); + } + /** * Creates a <var>DirectoryScanningTimerTask</var>. * @@ -126,8 +133,8 @@ public final class DirectoryScanningTimerTask extends TimerTask * to occur before the next error is logged (can be used to suppress error when the * directory is on a remote share and the server is flaky sometimes) */ - public DirectoryScanningTimerTask(IScannedStore scannedStore, File faultyPathDirectory, - IStoreHandler handler, int ignoredErrorCount) + public DirectoryScanningTimerTask(final IScannedStore scannedStore, + final File faultyPathDirectory, final IStoreHandler handler, final int ignoredErrorCount) { assert scannedStore != null; assert handler != null; @@ -145,10 +152,18 @@ public final class DirectoryScanningTimerTask extends TimerTask { return new IStoreHandler() { - public void handle(StoreItem item) + + // + // IStoreHandler + // + + public final void handle(final StoreItem item) { - File path = asFile(directory, item); - handler.handle(path); + final File path = asFile(directory, item); + if (handler.mayHandle(path)) + { + handler.handle(path); + } } }; } @@ -157,19 +172,20 @@ public final class DirectoryScanningTimerTask extends TimerTask { return new IScannedStore() { - public String getLocationDescription(StoreItem item) + public String getLocationDescription(final StoreItem item) { return DirectoryScanningTimerTask.getLocationDescription(asFile(item)); } - public boolean exists(StoreItem item) + public boolean exists(final StoreItem item) { return asFile(item).exists(); } - public StoreItem[] tryListSortedReadyToProcess(ISimpleLogger loggerOrNull) + public StoreItem[] tryListSortedReadyToProcess(final ISimpleLogger loggerOrNull) { - File[] files = FileUtilities.tryListFiles(directory, filter, loggerOrNull); + final File[] files = + FileUtilities.tryListFiles(directory, filter, loggerOrNull); if (files != null) { FileUtilities.sortByLastModified(files); @@ -180,9 +196,9 @@ public final class DirectoryScanningTimerTask extends TimerTask } } - private StoreItem[] asItems(File[] files) + private StoreItem[] asItems(final File[] files) { - StoreItem[] items = new StoreItem[files.length]; + final StoreItem[] items = new StoreItem[files.length]; for (int i = 0; i < items.length; i++) { items[i] = new StoreItem(files[i].getName()); @@ -190,19 +206,19 @@ public final class DirectoryScanningTimerTask extends TimerTask return items; } - private File asFile(StoreItem item) + private File asFile(final StoreItem item) { return DirectoryScanningTimerTask.asFile(directory, item); } }; } - private static String getLocationDescription(File file) + private static String getLocationDescription(final File file) { return file.getPath(); } - private static File asFile(File parentDirectory, StoreItem item) + private static File asFile(final File parentDirectory, final StoreItem item) { return new File(parentDirectory, item.getName()); } @@ -221,7 +237,7 @@ public final class DirectoryScanningTimerTask extends TimerTask } checkForFaultyPathsFileChanged(); final StoreItem[] paths = listFiles(); - for (StoreItem path : paths) + for (final StoreItem path : paths) { if (isFaultyPathsFile(path)) // Never touch the faultyPathsFile. { @@ -230,8 +246,9 @@ public final class DirectoryScanningTimerTask extends TimerTask try { handle(path); - } catch (Exception ex) // do not stop when processing of one file has failed, - // continue with other files + } catch (final Exception ex) // do not stop when processing of one file has + // failed, + // continue with other files { printNotification(ex); } @@ -241,21 +258,21 @@ public final class DirectoryScanningTimerTask extends TimerTask { operationLog.trace("Finished scanning directory " + sourceDirectory + "."); } - } catch (Exception ex) + } catch (final Exception ex) { printNotification(ex); } } - private void printNotification(Exception ex) + private void printNotification(final Exception ex) { notificationLog.error("An exception has occurred. (thread still running)", ex); } - private boolean isFaultyPathsFile(StoreItem item) + private boolean isFaultyPathsFile(final StoreItem item) { - String itemLocation = sourceDirectory.getLocationDescription(item); - String faultyPathsLocation = getLocationDescription(faultyPathsFile); + final String itemLocation = sourceDirectory.getLocationDescription(item); + final String faultyPathsLocation = getLocationDescription(faultyPathsFile); return itemLocation.equals(faultyPathsLocation); } @@ -264,7 +281,7 @@ public final class DirectoryScanningTimerTask extends TimerTask if (faultyPathsFile.exists()) { if (faultyPathsFile.lastModified() > faultyPathsLastChanged) // Handles manual - // manipulation. + // manipulation. { faultyPaths.clear(); CollectionIO.readCollection(faultyPathsFile, faultyPaths); @@ -286,8 +303,8 @@ public final class DirectoryScanningTimerTask extends TimerTask private StoreItem[] listFiles() { final boolean logNotifyError = (errorCountReadingDirectory == ignoredErrorCount); // Avoid - // mailbox - // flooding. + // mailbox + // flooding. final boolean logOperationError = (errorCountReadingDirectory < ignoredErrorCount); final ISimpleLogger errorLogger = logNotifyError ? createSimpleErrorLogger(LogCategory.NOTIFY) @@ -317,7 +334,7 @@ public final class DirectoryScanningTimerTask extends TimerTask { return new ISimpleLogger() { - public void log(LogLevel dummyLevel, String message) + public void log(final LogLevel dummyLevel, final String message) { if (category == LogCategory.NOTIFY) { @@ -330,7 +347,7 @@ public final class DirectoryScanningTimerTask extends TimerTask }; } - private void handle(StoreItem item) + private void handle(final StoreItem item) { if (isFaultyPath(item)) { // Guard: skip faulty paths. @@ -351,15 +368,15 @@ public final class DirectoryScanningTimerTask extends TimerTask } } - private boolean isFaultyPath(StoreItem item) + private boolean isFaultyPath(final StoreItem item) { - String path = sourceDirectory.getLocationDescription(item); + final String path = sourceDirectory.getLocationDescription(item); return faultyPaths.contains(path); } - private void addToFaultyPaths(StoreItem item) + private void addToFaultyPaths(final StoreItem item) { - String path = sourceDirectory.getLocationDescription(item); + final String path = sourceDirectory.getLocationDescription(item); faultyPaths.add(path); CollectionIO.writeIterable(faultyPathsFile, faultyPaths); faultyPathsLastChanged = faultyPathsFile.lastModified(); diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/IPathHandler.java b/common/source/java/ch/systemsx/cisd/common/utilities/IPathHandler.java index 8d6e777a0b455a17b6cb524ea535a983c648a9ad..19670dc0be85244da795e33d92feb8a9014de674 100644 --- a/common/source/java/ch/systemsx/cisd/common/utilities/IPathHandler.java +++ b/common/source/java/ch/systemsx/cisd/common/utilities/IPathHandler.java @@ -31,4 +31,12 @@ public interface IPathHandler * when the method returns. */ public void handle(File path); + + /** + * Whether given <var>path</var> may be handled or not. + * <p> + * This method is called just before {@link #handle(File)}. + * </p> + */ + public boolean mayHandle(File path); } \ No newline at end of file diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/WatermarkWatcher.java b/common/source/java/ch/systemsx/cisd/common/utilities/WatermarkWatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..7ef041964358139ec8eebd672cf1f473130bd65f --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/utilities/WatermarkWatcher.java @@ -0,0 +1,123 @@ +/* + * Copyright 2008 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.common.utilities; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.io.FileSystemUtils; +import org.apache.commons.io.FileUtils; +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; + +/** + * A watermark watcher. + * <p> + * This class is thread-safe. + * </p> + * + * @see FileSystemUtils + * @author Christian Ribeaud + */ +public final class WatermarkWatcher +{ + + private static final Logger notificationLog = + LogFactory.getLogger(LogCategory.NOTIFY, WatermarkWatcher.class); + + private static final Logger operationLog = + LogFactory.getLogger(LogCategory.OPERATION, WatermarkWatcher.class); + + private final long watermark; + + private boolean notified; + + /** + * @param watermark the watermark value in kilobytes. If negative, then + * {@link #isWatermarkReached(File)} always return <code>false</code>. + */ + public WatermarkWatcher(final long watermark) + { + this.watermark = watermark; + } + + private final static String displayKilobyteValue(final long value) + { + return FileUtils.byteCountToDisplaySize(value * FileUtils.ONE_KB); + } + + /** + * Checks whether the watermark value is reached. + * <p> + * If so, then informs (only once!) the administrator (via notification log) about it. The + * administrator gets also informed when the space is sufficient again. + * </p> + */ + public final synchronized boolean isWatermarkReached(final File path) + { + if (watermark < 0) + { + return false; + } + try + { + final String canonicalPath = path.getCanonicalPath(); + final long freeSpace = FileSystemUtils.freeSpaceKb(canonicalPath); + final String freeSpaceDisplayed = displayKilobyteValue(freeSpace); + if (freeSpace <= watermark) + { + if (notified == false) + { + notificationLog.warn(String.format( + "The amount of available space (%s) on '%s' " + + "is lower than the specified watermark (%s).", + freeSpaceDisplayed, canonicalPath, displayKilobyteValue(watermark))); + } + notified = true; + return true; + } else + { + if (notified == true) + { + notificationLog + .info(String + .format( + "The amount of available space (%s) on '%s' " + + "is again sufficient (greater than the specified watermark: %s).", + freeSpaceDisplayed, canonicalPath, + displayKilobyteValue(watermark))); + } else + { + if (operationLog.isInfoEnabled()) + { + operationLog.info(String.format( + "Amount of available space on '%s' is: %s.", canonicalPath, + freeSpaceDisplayed)); + } + } + notified = false; + return false; + } + } catch (IOException ex) + { + operationLog.error("The watermark watcher can not work properly.", ex); + return false; + } + } +} diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java index 32b983603e22cb2a0467f6f57249cbcbe7c18516..53df73c9f64dddf0b44ab32eecc5f34fc5f548af 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java @@ -73,7 +73,7 @@ public class DirectoryScanningTimerTaskTest /** * A mock implementation that stores the handled paths. */ - public static class MockPathHandler implements IPathHandler + private static class MockPathHandler implements IPathHandler { final List<File> handledPaths = new ArrayList<File>(); @@ -83,12 +83,21 @@ public class DirectoryScanningTimerTaskTest handledPaths.clear(); } - public void handle(File path) + // + // IPathHandler + // + + public void handle(final File path) { handledPaths.add(path); path.delete(); } + public final boolean mayHandle(final File path) + { + return true; + } + } @BeforeClass @@ -157,7 +166,7 @@ public class DirectoryScanningTimerTaskTest assert someFile.exists(); final DirectoryScanningTimerTask scanner = new DirectoryScanningTimerTask(workingDirectory, ACCEPT_ALL_FILTER, mockPathHandler); - String fileLocation = someFile.getPath(); + final String fileLocation = someFile.getPath(); CollectionIO.writeIterable(faultyPaths, Collections.singleton(fileLocation)); scanner.run(); assertEquals(0, mockPathHandler.handledPaths.size()); @@ -193,7 +202,7 @@ public class DirectoryScanningTimerTaskTest // See whether faulty_paths settings works. scanner.run(); assertEquals(0, mockPathHandler.handledPaths.size()); - List<String> faulty = CollectionIO.readList(faultyPaths); + final List<String> faulty = CollectionIO.readList(faultyPaths); assertEquals(1, faulty.size()); assertEquals(someFile.getPath(), faulty.get(0)); // See whether fault_paths resetting works.