# HG changeset patch # User Ingo Weinzierl # Date 1269616832 0 # Node ID eedad2ddad14bfdcd9bb53fd85690de876625190 # Parent d0a39efbfd96be0eea2b3873853d2b5c2b6a591d Removed race-condition while shapefile creation (issue164). gnv-artifacts/trunk@841 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r d0a39efbfd96 -r eedad2ddad14 gnv-artifacts/ChangeLog --- a/gnv-artifacts/ChangeLog Fri Mar 26 10:06:36 2010 +0000 +++ b/gnv-artifacts/ChangeLog Fri Mar 26 15:20:32 2010 +0000 @@ -1,3 +1,23 @@ +2010-03-26 Ingo Weinzierl + + Issue164 + + * src/main/java/de/intevation/gnv/utils/ExclusiveExec.java: New. This class + can be used to synchronize threads with a given key. To use this + synchronization, you just have to do something like the following: + UniqueKey token = ExclusiveExec.INSTANCE.acquire(key); + // your code to be synchronized + ExclusiveExec.INSTANCE.release(token); + A thread needs to wait if there is already a thread with the given key + which has acquired a token. Threads with a different key don't need to + wait for this thread. + + * src/main/java/de/intevation/gnv/state/profile/horizontalcrosssection/HorizontalCrossSectionMeshOutputState.java: + Synchronize shapefile writing of artifacts which have the same uuid. + + * src/main/java/de/intevation/gnv/utils/FileUtils.java: Added a function to + delete the content of a directory. + 2010-03-26 Ingo Weinzierl Issue211 (Applied patch) diff -r d0a39efbfd96 -r eedad2ddad14 gnv-artifacts/src/main/java/de/intevation/gnv/state/profile/horizontalcrosssection/HorizontalCrossSectionMeshOutputState.java --- a/gnv-artifacts/src/main/java/de/intevation/gnv/state/profile/horizontalcrosssection/HorizontalCrossSectionMeshOutputState.java Fri Mar 26 10:06:36 2010 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/state/profile/horizontalcrosssection/HorizontalCrossSectionMeshOutputState.java Fri Mar 26 15:20:32 2010 +0000 @@ -45,6 +45,7 @@ import de.intevation.gnv.state.exception.StateException; +import de.intevation.gnv.utils.ExclusiveExec; import de.intevation.gnv.utils.FileUtils; import de.intevation.gnv.utils.MapfileGenerator; import de.intevation.gnv.utils.MetaWriter; @@ -222,9 +223,11 @@ } else { AttributedPoint2ds result = getResult(uuid, callContext); + ExclusiveExec.UniqueKey k = ExclusiveExec.INSTANCE.acquire(uuid); if (result != null && (p = writeToShapeFile(uuid, result, callContext)) != null) { FileUtils.createZipArchive(new File(p), output); + ExclusiveExec.INSTANCE.release(k); } } } @@ -274,8 +277,10 @@ } else { AttributedPoint2ds result = getResult(uuid, callContext); + ExclusiveExec.UniqueKey key = ExclusiveExec.INSTANCE.acquire(uuid); if (result != null && (path = writeToShapeFile(uuid, result, callContext)) != null) { + ExclusiveExec.INSTANCE.release(key); String paramType = findParameterType(callContext); @@ -294,6 +299,7 @@ Document meta = MetaWriter.writeHorizontalcrosssectionMeta( callContext, uuid, path, paramType); + if (meta != null) { MapfileGenerator.getInstance().update(); return meta; @@ -349,13 +355,10 @@ try { synchronized (shapeFileLock) { - int count = 0; - while (shapeDir.exists()) { - shapeDir = new File(baseDir, uuid + "-" + count); - ++count; + if (shapeDir.exists()) { + FileUtils.deleteContent(shapeDir); } - - if (!shapeDir.mkdirs()) { + else if (!shapeDir.mkdirs()) { log.error("cannot create directory '" + shapeDir.getAbsolutePath() + "'"); return null; diff -r d0a39efbfd96 -r eedad2ddad14 gnv-artifacts/src/main/java/de/intevation/gnv/utils/ExclusiveExec.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/utils/ExclusiveExec.java Fri Mar 26 15:20:32 2010 +0000 @@ -0,0 +1,93 @@ +package de.intevation.gnv.utils; + +import java.util.HashMap; + +/** + * This class can be used to synchronize threads with a given key. To use this + * synchronization, you first need to do call {@link #acquire(String)} to + * retrieve a {@link UniqueKey}. After this, you can call the code being + * synchronized. After this execution, you need to call + * {@link #release(UniqueKey)} with your token you retrieved from {@link + * #acquire(String)}. A thread needs to wait for another thread if their keys + * are equal. Threads with different keys don't need to wait for each other. + * + * @author Sascha L. Teichmann (sascha.teichmann@intevation.de) + * @author Ingo Weinzierl (ingo.weinzierl@intevation.de) + */ +public final class ExclusiveExec +{ + public static final ExclusiveExec INSTANCE = new ExclusiveExec(); + + private HashMap tokens; + + public static class UniqueKey { + Object key; + int [] refs; + public UniqueKey(Object key) { + this.key = key; + refs = new int[1]; + } + } + + /** + * Private constructor. Use {@link #INSTANCE} instead. + */ + private ExclusiveExec() { + tokens = new HashMap(); + } + + /** + * This method serves a {@link UniqueKey} and starts a synchronized code + * block. + * + * @param key The key used to identify same threads. + * + * @return UniqueKey. Use this object to call {@link #release(UniqueKey)} + * at the end of your code being synchronized. + */ + public UniqueKey acquire(Object key) { + + try { + UniqueKey internalKey = null; + synchronized (tokens) { + internalKey = (UniqueKey)tokens.get(key); + + if (internalKey == null) { + tokens.put(key, internalKey = new UniqueKey(key)); + } + } + + synchronized (internalKey) { + ++internalKey.refs[0]; + while (internalKey.refs[0] > 1) { + internalKey.wait(10000L); + } + } + + return internalKey; + } + catch (InterruptedException ie) { + return null; + } + } + + /** + * This method releases a lock. Call this method at the end of your code + * being synchronized. + * + * @param internalKey Token retrieved by {@link #acquire(Object)}. + */ + public void release(UniqueKey internalKey) { + if (internalKey != null) { + synchronized (internalKey) { + if (--internalKey.refs[0] < 1) { + synchronized (tokens) { + tokens.remove(internalKey.key); + } + } + internalKey.notifyAll(); + } + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r d0a39efbfd96 -r eedad2ddad14 gnv-artifacts/src/main/java/de/intevation/gnv/utils/FileUtils.java --- a/gnv-artifacts/src/main/java/de/intevation/gnv/utils/FileUtils.java Fri Mar 26 10:06:36 2010 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/utils/FileUtils.java Fri Mar 26 15:20:32 2010 +0000 @@ -14,12 +14,28 @@ /** * @author Sascha L. Teichmann (sascha.teichmann@intevation.de) + * @author Ingo Weinzierl (ingo.weinzierl@intevation.de) */ public final class FileUtils { private FileUtils() { } + public final static void deleteContent(File dir) { + if (dir == null || !dir.isDirectory()) { + return; + } + + File[] files = dir.listFiles(); + if (files != null) { + for (File file: files) { + deleteRecursive(file); + } + } + + return; + } + public final static boolean deleteRecursive(File file) { if (file == null) {