diff --git a/common/source/java/ch/systemsx/cisd/common/highwatermark/DirectoryScanningChangeListener.java b/common/source/java/ch/systemsx/cisd/common/highwatermark/DirectoryScanningChangeListener.java deleted file mode 100644 index 7522a78d6db943ecd5a40f36c228cb155d3471d5..0000000000000000000000000000000000000000 --- a/common/source/java/ch/systemsx/cisd/common/highwatermark/DirectoryScanningChangeListener.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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.highwatermark; - -import java.io.File; -import java.util.HashSet; -import java.util.Set; - -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import ch.systemsx.cisd.common.highwatermark.HighwaterMarkWatcher.HighwaterMarkEvent; -import ch.systemsx.cisd.common.utilities.DirectoryScanningTimerTask; -import ch.systemsx.cisd.common.utilities.StoreItem; - -/** - * A <code>ChangeListener</code> implementation that informs the encapsulated - * {@link DirectoryScanningTimerTask} when the free space is again OK. - * <p> - * Subclasses will fill the unhandled paths set. - * </p> - * - * @author Christian Ribeaud - */ -public abstract class DirectoryScanningChangeListener implements ChangeListener -{ - private DirectoryScanningTimerTask directoryScanning; - - protected final Set<StoreItem> unhandledItems = new HashSet<StoreItem>(); - - protected DirectoryScanningChangeListener() - { - } - - /** - * Encapsulates given <var>path</var> in a <code>StoreItem</code> and returns it. - */ - protected final StoreItem asStoreItem(final File path) - { - return StoreItem.asItem(path); - } - - /** - * Sets the <code>DirectoryScanningTimerTask</code> that should get informed (and remove the - * unhandled paths from the faulty ones) when free space is again OK. - */ - public final void setDirectoryScanning(final DirectoryScanningTimerTask directoryScanning) - { - assert directoryScanning != null : "Unspecified DirectoryScanningTimerTask."; - this.directoryScanning = directoryScanning; - } - - // - // ChangeListener - // - - public final void stateChanged(final ChangeEvent e) - { - assert directoryScanning != null : "Unspecified DirectoryScanningTimerTask."; - final HighwaterMarkEvent event = (HighwaterMarkEvent) e; - if (event.isBelow() == false) - { - directoryScanning.removeFaultyPaths(unhandledItems.toArray(StoreItem.EMPTY_ARRAY)); - unhandledItems.clear(); - } - } - -} diff --git a/common/source/java/ch/systemsx/cisd/common/highwatermark/HighwaterMarkDirectoryScanningHandler.java b/common/source/java/ch/systemsx/cisd/common/highwatermark/HighwaterMarkDirectoryScanningHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..1e180a24091f31bd27cae171825fc70fcf31430e --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/highwatermark/HighwaterMarkDirectoryScanningHandler.java @@ -0,0 +1,94 @@ +/* + * 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.highwatermark; + +import java.io.File; + +import ch.systemsx.cisd.common.utilities.DirectoryScanningHandlerInterceptor; +import ch.systemsx.cisd.common.utilities.IDirectoryScanningHandler; +import ch.systemsx.cisd.common.utilities.StoreItem; +import ch.systemsx.cisd.common.utilities.DirectoryScanningTimerTask.IScannedStore; + +/** + * A <code>DirectoryScanningHandlerInterceptor</code> extension which bases its decision on the + * encapsulated {@link HighwaterMarkWatcher} and {@link FileWithHighwaterMark}s. + * <p> + * Note that the decision has precedence over encapsulated {@link IDirectoryScanningHandler} + * implementation and might short-circuit it. + * </p> + * + * @author Christian Ribeaud + */ +public final class HighwaterMarkDirectoryScanningHandler extends + DirectoryScanningHandlerInterceptor +{ + private final HighwaterMarkWatcher highwaterMarkWatcher; + + private final File[] files; + + public HighwaterMarkDirectoryScanningHandler( + final IDirectoryScanningHandler directoryScanningHandler, + final HighwaterMarkWatcher highwaterMarkWatcher, final File... files) + { + super(directoryScanningHandler); + assert directoryScanningHandler != null : "Unspecified IDirectoryScanningHandler"; + assert highwaterMarkWatcher != null : "Unspecified HighwaterMarkWatcher"; + assert files != null : "Unspecified files"; + this.highwaterMarkWatcher = highwaterMarkWatcher; + this.files = files; + } + + private final boolean mayHandle() + { + if (files.length < 1) + { + return isBelow(); + } + for (final File file : files) + { + if (isBelow(file)) + { + return false; + } + } + return true; + } + + private final boolean isBelow() + { + // The path has probably been set before. + highwaterMarkWatcher.run(); + return highwaterMarkWatcher.isBelow(); + } + + private final boolean isBelow(final File path) + { + highwaterMarkWatcher.setPathAndRun(path); + return highwaterMarkWatcher.isBelow(); + } + + // + // IDirectoryScanningHandler + // + + @Override + public final boolean mayHandle(final IScannedStore scannedStore, final StoreItem storeItem) + { + return mayHandle() == false ? false : super.mayHandle(scannedStore, storeItem); + } + +} diff --git a/common/source/java/ch/systemsx/cisd/common/highwatermark/PathHandlerInterceptor.java b/common/source/java/ch/systemsx/cisd/common/highwatermark/PathHandlerInterceptor.java deleted file mode 100644 index 65b6b23ed550e915185823903e75b8f8aa7165a3..0000000000000000000000000000000000000000 --- a/common/source/java/ch/systemsx/cisd/common/highwatermark/PathHandlerInterceptor.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.highwatermark; - -import java.io.File; - -import ch.systemsx.cisd.common.utilities.IPathHandler; - -/** - * An <code>IPathHandler</code> implementation which collects the unhandled paths before - * delegating the calls. - * - * @author Christian Ribeaud - */ -public final class PathHandlerInterceptor extends DirectoryScanningChangeListener implements - IPathHandler -{ - private final IPathHandler pathHandler; - - public PathHandlerInterceptor(final IPathHandler pathHandler) - { - this.pathHandler = pathHandler; - } - - // - // IPathHandler - // - - public final void handle(final File path) - { - pathHandler.handle(path); - } - - public final boolean mayHandle(final File path) - { - final boolean mayHandle = pathHandler.mayHandle(path); - if (mayHandle == false) - { - unhandledItems.add(asStoreItem(path)); - } - return mayHandle; - } -} \ No newline at end of file diff --git a/common/source/java/ch/systemsx/cisd/common/highwatermark/StoreHandlerInterceptor.java b/common/source/java/ch/systemsx/cisd/common/highwatermark/StoreHandlerInterceptor.java deleted file mode 100644 index cd6f45670ddc905231d1b1caf9c9a7e26783c93c..0000000000000000000000000000000000000000 --- a/common/source/java/ch/systemsx/cisd/common/highwatermark/StoreHandlerInterceptor.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.highwatermark; - -import ch.systemsx.cisd.common.utilities.IStoreHandler; -import ch.systemsx.cisd.common.utilities.StoreItem; - -/** - * An <code>IStoreHandler</code> implementation which collects the unhandled store items before - * delegating the calls. - * - * @author Christian Ribeaud - */ -public final class StoreHandlerInterceptor extends DirectoryScanningChangeListener implements - IStoreHandler -{ - private final IStoreHandler storeHandler; - - public StoreHandlerInterceptor(final IStoreHandler storeHandler) - { - super(); - this.storeHandler = storeHandler; - } - - // - // IStoreHandler - // - - public final void handle(final StoreItem item) - { - storeHandler.handle(item); - } - - public final boolean mayHandle(final StoreItem item) - { - final boolean mayHandle = storeHandler.mayHandle(item); - if (mayHandle == false) - { - unhandledItems.add(item); - } - return mayHandle; - } - -} diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningHandlerInterceptor.java b/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningHandlerInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..fbb6ae8cfdeb6513bd147d09e9aa0ec99d383992 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningHandlerInterceptor.java @@ -0,0 +1,56 @@ +/* + * 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 ch.systemsx.cisd.common.utilities.DirectoryScanningTimerTask.IScannedStore; + +/** + * An <code>IDirectoryScanningHandler</code> implementation which simply delegates the method + * calls to the encapsulated implementation. + * + * @author Christian Ribeaud + */ +public class DirectoryScanningHandlerInterceptor implements IDirectoryScanningHandler +{ + private final IDirectoryScanningHandler directoryScanningHandler; + + protected DirectoryScanningHandlerInterceptor( + final IDirectoryScanningHandler directoryScanningHandler) + { + assert directoryScanningHandler != null : "Unspecified IDirectoryScanningHandler implementation"; + this.directoryScanningHandler = directoryScanningHandler; + } + + // + // IDirectoryScanningHandler + // + + public void beforeHandle() + { + directoryScanningHandler.beforeHandle(); + } + + public boolean mayHandle(final IScannedStore scannedStore, final StoreItem storeItem) + { + return directoryScanningHandler.mayHandle(scannedStore, storeItem); + } + + public void finishItemHandle(final IScannedStore scannedStore, final StoreItem storeItem) + { + directoryScanningHandler.finishItemHandle(scannedStore, storeItem); + } +} 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 10c7a70be56fb46966d2970bd713c47bf49425fa..d37dc555268a7ae051eb087ea285ac2cdc5fa9b1 100644 --- a/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java +++ b/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java @@ -18,14 +18,10 @@ package ch.systemsx.cisd.common.utilities; import java.io.File; import java.io.FileFilter; -import java.util.HashSet; -import java.util.Set; import java.util.TimerTask; import org.apache.log4j.Logger; -import ch.systemsx.cisd.common.collections.CollectionIO; -import ch.systemsx.cisd.common.collections.CollectionUtils; import ch.systemsx.cisd.common.logging.ISimpleLogger; import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; @@ -39,21 +35,20 @@ import ch.systemsx.cisd.common.logging.LogLevel; * <p> * The class should be constructed in the start-up phase and as part of the system's self-test in * order to reveal problems with incorrect paths timely. + * </p> * * @author Bernd Rinn */ public final class DirectoryScanningTimerTask extends TimerTask { - static final String FAULTY_PATH_FILENAME = ".faulty_paths"; - private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, DirectoryScanningTimerTask.class); private static final Logger notificationLog = LogFactory.getLogger(LogCategory.NOTIFY, DirectoryScanningTimerTask.class); - private final IStoreHandler handler; + private final IStoreHandler storeHandler; private final IScannedStore sourceDirectory; @@ -65,11 +60,7 @@ public final class DirectoryScanningTimerTask extends TimerTask private int errorCountReadingDirectory; - private final Set<String> faultyPaths; - - private final File faultyPathsFile; - - private long faultyPathsLastChanged; + private final IDirectoryScanningHandler directoryScanningHandler; /** * Creates a <var>DirectoryScanningTimerTask</var>. @@ -77,59 +68,99 @@ public final class DirectoryScanningTimerTask extends TimerTask * @param sourceDirectory The directory to scan for entries. * @param filter The file filter that picks the entries to handle. * @param handler The handler that is used for treating the matching paths. + * @param directoryScanningHandler A directory scanning handler. */ public DirectoryScanningTimerTask(final File sourceDirectory, final FileFilter filter, - final IPathHandler handler) + final IPathHandler handler, final IDirectoryScanningHandler directoryScanningHandler) { - this(sourceDirectory, filter, handler, 0); + this(asScannedStore(sourceDirectory, filter), directoryScanningHandler, PathHandlerAdapter + .asScanningHandler(sourceDirectory, handler), 0); } /** * Creates a <var>DirectoryScanningTimerTask</var>. * + * @param scannedStore The store which is scan for entries. * @param sourceDirectory The directory to scan for entries. - * @param filter The file filter that picks the entries to handle. - * @param handler The handler that is used for treating the matching paths. + * @param storeHandler The handler that is used for treating the matching paths. * @param ignoredErrorCount The number of consecutive errors of reading the directory that need * 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(final File sourceDirectory, final FileFilter filter, - final IPathHandler handler, final int ignoredErrorCount) + public DirectoryScanningTimerTask(final IScannedStore scannedStore, final File sourceDirectory, + final IStoreHandler storeHandler, final int ignoredErrorCount) { - this(asScannedStore(sourceDirectory, filter), sourceDirectory, PathHandlerAdapter - .asScanningHandler(sourceDirectory, handler), ignoredErrorCount); + this(scannedStore, new FaultyPathHandler(sourceDirectory), storeHandler, ignoredErrorCount); } + /** + * Creates a <var>DirectoryScanningTimerTask</var>. + * + * @param sourceDirectory The directory to scan for entries. + * @param storeHandler The handler that is used for treating the matching paths. + * @param directoryScanningHandler A directory scanning handler. + */ public DirectoryScanningTimerTask(final File sourceDirectory, final FileFilter filter, - final IStoreHandler handler) + final IStoreHandler storeHandler, + final IDirectoryScanningHandler directoryScanningHandler) + { + this(asScannedStore(sourceDirectory, filter), directoryScanningHandler, storeHandler, 0); + } + + /** + * Creates a <var>DirectoryScanningTimerTask</var>. + * + * @param sourceDirectory The directory to scan for entries. + * @param fileFilter The file filter that picks the entries to handle. + * @param pathHandler The handler that is used for treating the matching paths. + * @param ignoredErrorCount The number of consecutive errors of reading the directory that need + * 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) + */ + DirectoryScanningTimerTask(final File sourceDirectory, final FileFilter fileFilter, + final IPathHandler pathHandler, final int ignoredErrorCount) + { + this(asScannedStore(sourceDirectory, fileFilter), new FaultyPathHandler(sourceDirectory), + PathHandlerAdapter.asScanningHandler(sourceDirectory, pathHandler), + ignoredErrorCount); + } + + /** + * Creates a <var>DirectoryScanningTimerTask</var>. + * + * @param sourceDirectory The directory to scan for entries. + * @param filter The file filter that picks the entries to handle. + * @param pathHandler The handler that is used for treating the matching paths. + */ + DirectoryScanningTimerTask(final File sourceDirectory, final FileFilter filter, + final IPathHandler pathHandler) { - this(asScannedStore(sourceDirectory, filter), sourceDirectory, handler, 0); + this(sourceDirectory, filter, pathHandler, 0); } /** * Creates a <var>DirectoryScanningTimerTask</var>. * * @param scannedStore The store which is scan for entries. - * @param faultyPathDirectory The directory in which file with faulty paths is should be stored. - * @param handler The handler that is used for treating the matching paths. + * @param directoryScanningHandler A directory scanning handler. + * @param storeHandler The handler that is used for treating the matching paths. * @param ignoredErrorCount The number of consecutive errors of reading the directory that need * 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(final IScannedStore scannedStore, - final File faultyPathDirectory, final IStoreHandler handler, final int ignoredErrorCount) + private DirectoryScanningTimerTask(final IScannedStore scannedStore, + final IDirectoryScanningHandler directoryScanningHandler, + final IStoreHandler storeHandler, final int ignoredErrorCount) { assert scannedStore != null; - assert handler != null; + assert storeHandler != null; + assert directoryScanningHandler != null : "Unspecified IDirectoryScanningHandler implementation"; assert ignoredErrorCount >= 0; this.ignoredErrorCount = ignoredErrorCount; this.sourceDirectory = scannedStore; - this.handler = handler; - this.faultyPaths = new HashSet<String>(); - this.faultyPathsFile = new File(faultyPathDirectory, FAULTY_PATH_FILENAME); - faultyPathsFile.delete(); + this.storeHandler = storeHandler; + this.directoryScanningHandler = directoryScanningHandler; } private final static IScannedStore asScannedStore(final File directory, final FileFilter filter) @@ -137,50 +168,51 @@ public final class DirectoryScanningTimerTask extends TimerTask return new DirectoryScannedStore(filter, directory); } - private final static String getLocationDescription(final File file) - { - return file.getPath(); - } - /** * Handles all entries in the source directory that are picked by the filter. */ @Override public final void run() { + if (operationLog.isTraceEnabled()) + { + operationLog.trace("Start scanning directory " + sourceDirectory + "."); + } try { - if (operationLog.isTraceEnabled()) + final StoreItem[] storeItems = listStoreItems(); + directoryScanningHandler.beforeHandle(); + for (final StoreItem storeItem : storeItems) { - operationLog.trace("Start scanning directory " + sourceDirectory + "."); - } - checkForFaultyPathsFileChanged(); - final StoreItem[] paths = listFiles(); - for (final StoreItem path : paths) - { - if (isFaultyPathsFile(path)) // Never touch the faultyPathsFile. - { - continue; - } - try - { - handle(path); - } catch (final Exception ex) // do not stop when processing of one file has - // failed, - // continue with other files + if (directoryScanningHandler.mayHandle(sourceDirectory, storeItem)) { - printNotification(ex); + try + { + storeHandler.handle(storeItem); + if (operationLog.isDebugEnabled()) + { + operationLog.debug(String.format( + "Following store item '%s' has been handled.", storeItem)); + } + } catch (final Exception ex) + { + // Do not stop when processing of one file has failed, + // continue with other files. + printNotification(ex); + } finally + { + directoryScanningHandler.finishItemHandle(sourceDirectory, storeItem); + } } - - } - if (operationLog.isTraceEnabled()) - { - operationLog.trace("Finished scanning directory " + sourceDirectory + "."); } } catch (final Exception ex) { printNotification(ex); } + if (operationLog.isTraceEnabled()) + { + operationLog.trace("Finished scanning directory " + sourceDirectory + "."); + } } private final void printNotification(final Exception ex) @@ -188,50 +220,17 @@ public final class DirectoryScanningTimerTask extends TimerTask notificationLog.error("An exception has occurred. (thread still running)", ex); } - private boolean isFaultyPathsFile(final StoreItem item) + private final StoreItem[] listStoreItems() { - final String itemLocation = sourceDirectory.getLocationDescription(item); - final String faultyPathsLocation = getLocationDescription(faultyPathsFile); - return itemLocation.equals(faultyPathsLocation); - } - - private final void checkForFaultyPathsFileChanged() - { - if (faultyPathsFile.exists()) - { - if (faultyPathsFile.lastModified() > faultyPathsLastChanged) // Handles manual - // manipulation. - { - faultyPaths.clear(); - CollectionIO.readCollection(faultyPathsFile, faultyPaths); - faultyPathsLastChanged = faultyPathsFile.lastModified(); - if (operationLog.isInfoEnabled()) - { - operationLog.info(String.format( - "Reread faulty paths file (%s), new set contains %d entries", - getLocationDescription(faultyPathsFile), faultyPaths.size())); - } - } - } else - // Handles manual removal. - { - faultyPaths.clear(); - } - } - - private final StoreItem[] listFiles() - { - final boolean logNotifyError = (errorCountReadingDirectory == ignoredErrorCount); // Avoid - // mailbox - // flooding. + // Avoid mailbox flooding. + final boolean logNotifyError = (errorCountReadingDirectory == ignoredErrorCount); final boolean logOperationError = (errorCountReadingDirectory < ignoredErrorCount); final ISimpleLogger errorLogger = logNotifyError ? createSimpleErrorLogger(LogCategory.NOTIFY) : (logOperationError ? createSimpleErrorLogger(LogCategory.OPERATION) : null); - - final StoreItem[] paths = sourceDirectory.tryListSortedReadyToProcess(errorLogger); - if (errorCountReadingDirectory > ignoredErrorCount && paths != null) + final StoreItem[] storeItems = sourceDirectory.tryListSortedReadyToProcess(errorLogger); + if (errorCountReadingDirectory > ignoredErrorCount && storeItems != null) { if (notificationLog.isInfoEnabled()) { @@ -239,14 +238,14 @@ public final class DirectoryScanningTimerTask extends TimerTask sourceDirectory)); } } - if (paths == null) + if (storeItems == null) { ++errorCountReadingDirectory; } else { errorCountReadingDirectory = 0; } - return (paths == null) ? new StoreItem[0] : paths; + return (storeItems == null) ? StoreItem.EMPTY_ARRAY : storeItems; } private final ISimpleLogger createSimpleErrorLogger(final LogCategory category) @@ -271,79 +270,6 @@ public final class DirectoryScanningTimerTask extends TimerTask }; } - private final void handle(final StoreItem item) - { - if (isFaultyPath(item)) - { // Guard: skip faulty paths. - return; - } - try - { - final boolean mayHandle = handler.mayHandle(item); - if (mayHandle) - { - handler.handle(item); - } - if (operationLog.isDebugEnabled()) - { - operationLog.debug(String.format("Following store item '%s' has %sbeen handled.", - item, mayHandle ? "" : "NOT ")); - } - } finally - { - // If the item still exists, we assume that it has not been handled. So it should be - // added to the faulty - // paths. - if (sourceDirectory.exists(item)) - { - addToFaultyPaths(item); - } - } - } - - private final boolean isFaultyPath(final StoreItem item) - { - final String path = sourceDirectory.getLocationDescription(item); - return faultyPaths.contains(path); - } - - private final void addToFaultyPaths(final StoreItem item) - { - final String path = sourceDirectory.getLocationDescription(item); - faultyPaths.add(path); - refreshFaultyPathsFile(); - } - - private final void refreshFaultyPathsFile() - { - CollectionIO.writeIterable(faultyPathsFile, faultyPaths); - faultyPathsLastChanged = faultyPathsFile.lastModified(); - } - - /** Removes given <var>storeItems</var> from the set of faulty ones. */ - public final void removeFaultyPaths(final StoreItem... storeItems) - { - assert storeItems != null : "Unspecified store items."; - final int size = storeItems.length; - if (size == 0) - { - return; - } - final Set<String> paths = new HashSet<String>(size); - for (final StoreItem storeItem : storeItems) - { - paths.add(sourceDirectory.getLocationDescription(storeItem)); - } - faultyPaths.removeAll(paths); - if (operationLog.isDebugEnabled()) - { - operationLog.debug(String.format( - "Following paths %s have been removed from the the faulty ones.", - CollectionUtils.abbreviate(paths, 10))); - } - refreshFaultyPathsFile(); - } - // // Helper classes // diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/FaultyPathHandler.java b/common/source/java/ch/systemsx/cisd/common/utilities/FaultyPathHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..c2f9a52c19b43864e312212ca9d32ac7b1718dab --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/utilities/FaultyPathHandler.java @@ -0,0 +1,141 @@ +/* + * 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.util.HashSet; +import java.util.Set; + +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.collections.CollectionIO; +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.common.utilities.DirectoryScanningTimerTask.IScannedStore; + +/** + * An <code>IDirectoryScanningHandler</code> which manages faulty paths. + * <p> + * A faulty path is a {@link StoreItem} which was <i>not handled</i> (still exists in the + * {@link IScannedStore}). + * </p> + * + * @author Christian Ribeaud + */ +public final class FaultyPathHandler implements IDirectoryScanningHandler +{ + + private static final Logger operationLog = + LogFactory.getLogger(LogCategory.OPERATION, FaultyPathHandler.class); + + private final Set<String> faultyPaths; + + private final File faultyPathsFile; + + private long faultyPathsLastChanged; + + static final String FAULTY_PATH_FILENAME = ".faulty_paths"; + + public FaultyPathHandler(final File faultyPathDirectory) + { + this.faultyPaths = new HashSet<String>(); + this.faultyPathsFile = new File(faultyPathDirectory, FAULTY_PATH_FILENAME); + faultyPathsFile.delete(); + } + + private final void checkForFaultyPathsFileChanged() + { + if (faultyPathsFile.exists()) + { + // Handles manual manipulation. + if (faultyPathsFile.lastModified() > faultyPathsLastChanged) + { + faultyPaths.clear(); + CollectionIO.readCollection(faultyPathsFile, faultyPaths); + faultyPathsLastChanged = faultyPathsFile.lastModified(); + if (operationLog.isInfoEnabled()) + { + operationLog.info(String.format( + "Reread faulty paths file (%s), new set contains %d entries", + getLocationDescription(faultyPathsFile), faultyPaths.size())); + } + } + } else + { + // Handles manual removal. + faultyPaths.clear(); + } + } + + private final static String getLocationDescription(final File file) + { + return file.getPath(); + } + + private final boolean isFaultyPath(final IScannedStore scannedStore, final StoreItem storeItem) + { + final String path = scannedStore.getLocationDescription(storeItem); + return faultyPaths.contains(path); + } + + private final boolean isFaultyPathsFile(final IScannedStore scannedStore, + final StoreItem storeItem) + { + final String itemLocation = scannedStore.getLocationDescription(storeItem); + final String faultyPathsLocation = getLocationDescription(faultyPathsFile); + return itemLocation.equals(faultyPathsLocation); + } + + private final void addToFaultyPaths(final IScannedStore scannedStore, final StoreItem item) + { + final String path = scannedStore.getLocationDescription(item); + faultyPaths.add(path); + refreshFaultyPathsFile(); + } + + private final void refreshFaultyPathsFile() + { + CollectionIO.writeIterable(faultyPathsFile, faultyPaths); + faultyPathsLastChanged = faultyPathsFile.lastModified(); + } + + // + // IDirectoryScanningHandler + // + + public final void beforeHandle() + { + checkForFaultyPathsFileChanged(); + } + + public final boolean mayHandle(final IScannedStore scannedStore, final StoreItem storeItem) + { + return isFaultyPath(scannedStore, storeItem) == false + && isFaultyPathsFile(scannedStore, storeItem) == false; + } + + public final void finishItemHandle(final IScannedStore scannedStore, final StoreItem storeItem) + { + // If the item still exists, we assume that it has not been handled. So it + // should be added to the faulty paths. + if (scannedStore.exists(storeItem)) + { + addToFaultyPaths(scannedStore, storeItem); + } + } + +} diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/IDirectoryScanningHandler.java b/common/source/java/ch/systemsx/cisd/common/utilities/IDirectoryScanningHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..db98d072f454f3c8512cffb88fa160ca7374805d --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/utilities/IDirectoryScanningHandler.java @@ -0,0 +1,46 @@ +/* + * 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 ch.systemsx.cisd.common.utilities.DirectoryScanningTimerTask.IScannedStore; + +/** + * A helper class for {@link DirectoryScanningTimerTask} which performs operations before and after + * treating the matching paths. + * + * @author Christian Ribeaud + * @see DirectoryScanningTimerTask + */ +public interface IDirectoryScanningHandler +{ + + /** + * Is performed just before handling all the items contained in the store. + */ + public void beforeHandle(); + + /** + * Whether given <code>storeItem</code> found in given <var>scannedStore</var> should be + * processed or not. + */ + public boolean mayHandle(IScannedStore scannedStore, StoreItem storeItem); + + /** + * Finishes and closes the handling of given <var>storeItem</var>. + */ + public void finishItemHandle(IScannedStore scannedStore, StoreItem storeItem); +} 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 d775b9049a87fa6e27ea523577c001caf04d4a9b..3380e2f31d489474eb6c0f791bbcb55725274bde 100644 --- a/common/source/java/ch/systemsx/cisd/common/utilities/IPathHandler.java +++ b/common/source/java/ch/systemsx/cisd/common/utilities/IPathHandler.java @@ -32,12 +32,4 @@ 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/IStoreHandler.java b/common/source/java/ch/systemsx/cisd/common/utilities/IStoreHandler.java index 28a8a9c8a811f4e60ec0a300f9990a51dff09440..0ff94bca6176c7ab7ba3abc252a523e982462ce2 100644 --- a/common/source/java/ch/systemsx/cisd/common/utilities/IStoreHandler.java +++ b/common/source/java/ch/systemsx/cisd/common/utilities/IStoreHandler.java @@ -35,12 +35,4 @@ public interface IStoreHandler * gone when the method returns. */ void handle(StoreItem item); - - /** - * Whether given <var>item</var> may be handled or not. - * <p> - * This method is called just before {@link #handle(StoreItem)}. - * </p> - */ - boolean mayHandle(StoreItem item); } diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/PathHandlerAdapter.java b/common/source/java/ch/systemsx/cisd/common/utilities/PathHandlerAdapter.java index 1d3e9b23be17f46331dc24ce98d6dd8b787c1308..43e0a20ce9d5492b94f8ea8fd45bfb2e63dd4b2d 100644 --- a/common/source/java/ch/systemsx/cisd/common/utilities/PathHandlerAdapter.java +++ b/common/source/java/ch/systemsx/cisd/common/utilities/PathHandlerAdapter.java @@ -49,11 +49,6 @@ public class PathHandlerAdapter implements IStoreHandler // IStoreHandler // - public final boolean mayHandle(final StoreItem item) - { - return pathHandler.mayHandle(asFile(item)); - } - public final void handle(final StoreItem item) { pathHandler.handle(asFile(item)); diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/highwatermark/HighwaterMarkDirectoryScanningHandlerTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/highwatermark/HighwaterMarkDirectoryScanningHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..94502ec2d320bbb9b40d360faeeda9ba995a801d --- /dev/null +++ b/common/sourceTest/java/ch/systemsx/cisd/common/highwatermark/HighwaterMarkDirectoryScanningHandlerTest.java @@ -0,0 +1,44 @@ +/* + * 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.highwatermark; + +import static org.testng.AssertJUnit.assertFalse; + +import org.testng.annotations.Test; + +/** + * Test cases for the {@link HighwaterMarkDirectoryScanningHandler}. + * + * @author Christian Ribeaud + */ +public final class HighwaterMarkDirectoryScanningHandlerTest +{ + + @Test + public final void testConstructor() + { + boolean fail = true; + try + { + new HighwaterMarkDirectoryScanningHandler(null, null); + } catch (final AssertionError ex) + { + fail = false; + } + assertFalse(fail); + } +} \ No newline at end of file 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 5e9bb5ff9f67427f60a06445cc092f9444770fc0..ad72e75ef7ad4097b9e151cb606840de38378c51 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java @@ -78,18 +78,11 @@ public class DirectoryScanningTimerTaskTest private final List<File> handledPaths = new ArrayList<File>(); - private boolean mayHandle = true; - public void clear() { handledPaths.clear(); } - public final void setMayHandle(final boolean mayHandle) - { - this.mayHandle = mayHandle; - } - // // IPathHandler // @@ -99,12 +92,6 @@ public class DirectoryScanningTimerTaskTest handledPaths.add(path); path.delete(); } - - public final boolean mayHandle(final File path) - { - return mayHandle; - } - } @BeforeClass @@ -126,8 +113,7 @@ public class DirectoryScanningTimerTaskTest private final File getFaultyPathFile() { - final File faultyPaths = - new File(workingDirectory, DirectoryScanningTimerTask.FAULTY_PATH_FILENAME); + final File faultyPaths = new File(workingDirectory, FaultyPathHandler.FAULTY_PATH_FILENAME); return faultyPaths; } @@ -422,35 +408,4 @@ public class DirectoryScanningTimerTaskTest LogMonitoringAppender.removeAppender(appender); } } - - @Test - public final void testRemoveFaultyPaths() throws IOException - { - final MockPathHandler pathHandler = new MockPathHandler(); - pathHandler.setMayHandle(false); - final DirectoryScanningTimerTask directoryScanning = - new DirectoryScanningTimerTask(workingDirectory, ACCEPT_ALL_FILTER, pathHandler); - testPathOrder(); - // No faulty file at this point. - assertEquals(1, workingDirectory.listFiles().length); - directoryScanning.run(); - // One faulty file found (as mayHandle is set to false). - List<String> faulty = CollectionIO.readList(getFaultyPathFile()); - assertEquals(1, faulty.size()); - final File file = new File(faulty.get(0)); - directoryScanning.removeFaultyPaths(StoreItem.asItem(file)); - faulty = CollectionIO.readList(getFaultyPathFile()); - // Faulty file is empty. - assertEquals(0, faulty.size()); - // Two files found (inclusive the faulty file). - assertEquals(2, workingDirectory.listFiles().length); - pathHandler.setMayHandle(true); - directoryScanning.run(); - faulty = CollectionIO.readList(getFaultyPathFile()); - assertEquals(0, faulty.size()); - // Only the faulty file present now. - final File[] files = workingDirectory.listFiles(); - assertEquals(1, files.length); - assertEquals(DirectoryScanningTimerTask.FAULTY_PATH_FILENAME, files[0].getName()); - } }