001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.data.shapefile;
017:
018: import java.io.IOException;
019: import java.util.HashMap;
020: import java.util.Map;
021: import java.util.logging.Logger;
022:
023: /**
024: * A read-write lock for shapefiles so that OS file locking exceptions will not
025: * ruin an attempt to update a shapefile. On windows there are often operating
026: * system locking conflicts when writing to a shapefile. In order to not have
027: * exceptions thrown everytime a write is made, geotools has implemented file
028: * locking for shapefiles.
029: *
030: * @author jeichar
031: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/plugin/shapefile/src/main/java/org/geotools/data/shapefile/Lock.java $
032: */
033: public class Lock {
034:
035: Logger logger = org.geotools.util.logging.Logging
036: .getLogger("org.geotools.data.shapefile");
037:
038: /**
039: * indicates a write is occurring
040: */
041: int writeLocks = 0;
042:
043: /**
044: * if not null a writer is waiting for the lock or is writing.
045: */
046: Thread writer;
047:
048: /**
049: * Thread->Owner map. If empty no read locks exist.
050: */
051: Map owners = new HashMap();
052:
053: /**
054: * If the lock can be read locked the lock will be read and default
055: * visibility for tests
056: *
057: * @throws IOException
058: */
059: synchronized boolean canRead() throws IOException {
060: if (writer != null && writer != Thread.currentThread())
061: return false;
062: if (writer == null)
063: return true;
064:
065: if (owners.size() > 1)
066: return false;
067:
068: return true;
069: }
070:
071: /**
072: * If the lock can be read locked the lock will be read and default
073: * visibility for tests
074: *
075: * @throws IOException
076: */
077: synchronized boolean canWrite() throws IOException {
078: if (owners.size() > 1)
079: return false;
080: if ((canRead())
081: && (writer == Thread.currentThread() || writer == null)) {
082: if (owners.isEmpty())
083: return true;
084: if (owners.containsKey(Thread.currentThread()))
085: return true;
086: }
087: return false;
088: }
089:
090: /**
091: * Called by shapefileReader before a read is started and before an IOStream
092: * is openned.
093: *
094: * @throws IOException
095: */
096: public synchronized void lockRead() throws IOException {
097: if (!canRead()) {
098: while (writeLocks > 0 || writer != null) {
099: try {
100: wait();
101: } catch (InterruptedException e) {
102: throw (IOException) new IOException().initCause(e);
103: }
104: }
105: }
106:
107: assertTrue(
108: "A write lock exists that is owned by another thread",
109: canRead());
110: Thread current = Thread.currentThread();
111: Owner owner = (Owner) owners.get(current);
112: if (owner != null) {
113: owner.timesLocked++;
114: } else {
115: owner = new Owner(current);
116: owners.put(current, owner);
117: }
118:
119: logger.finer("Start Read Lock:" + owner);
120: }
121:
122: private void assertTrue(String message, boolean b) {
123: if (!b) {
124: throw new AssertionError(message);
125: }
126: }
127:
128: /**
129: * Called by ShapefileReader after a read is complete and after the IOStream
130: * is closed.
131: */
132: public synchronized void unlockRead() {
133:
134: assertTrue("Current thread does not have a readLock", owners
135: .containsKey(Thread.currentThread()));
136:
137: Owner owner = (Owner) owners.get(Thread.currentThread());
138: assertTrue("Current thread has " + owner.timesLocked
139: + "negative number of locks", owner.timesLocked > 0);
140:
141: owner.timesLocked--;
142: if (owner.timesLocked == 0)
143: owners.remove(Thread.currentThread());
144:
145: notifyAll();
146:
147: logger.finer("unlock Read:" + owner);
148: }
149:
150: /**
151: * Called by ShapefileDataStore before a write is started and before an
152: * IOStream is openned.
153: *
154: * @throws IOException
155: */
156: public synchronized void lockWrite() throws IOException {
157: Thread currentThread = Thread.currentThread();
158: if (writer == null)
159: writer = currentThread;
160: while (!canWrite()) {
161: try {
162: wait();
163: } catch (InterruptedException e) {
164: throw (IOException) new IOException().initCause(e);
165: }
166:
167: if (writer == null)
168: writer = currentThread;
169: }
170:
171: if (writer == null)
172: writer = currentThread;
173:
174: assertTrue("The current thread is not the writer",
175: writer == currentThread);
176: assertTrue(
177: "There are read locks not belonging to the current thread.",
178: canRead());
179:
180: writeLocks++;
181: logger.finer(currentThread.getName()
182: + " is getting write lock:" + writeLocks);
183: }
184:
185: private class Owner {
186: final Thread owner;
187:
188: int timesLocked;
189:
190: Owner(Thread owner) {
191: this .owner = owner;
192: timesLocked = 1;
193: }
194:
195: public String toString() {
196: return owner.getName() + " has " + timesLocked + " locks";
197: }
198: }
199:
200: /**
201: * default visibility for tests
202: *
203: */
204: synchronized int getReadLocks(Thread thread) {
205: Owner owner = (Owner) owners.get(thread);
206: if (owner == null)
207: return -1;
208: return owner.timesLocked;
209: }
210:
211: public synchronized void unlockWrite() {
212: if (writeLocks > 0) {
213: assertTrue("current thread does not own the write lock",
214: writer == Thread.currentThread());
215: assertTrue("writeLock has already been unlocked",
216: writeLocks > 0);
217: writeLocks--;
218: if (writeLocks == 0)
219: writer = null;
220: }
221: logger
222: .finer("unlock write:"
223: + Thread.currentThread().getName());
224: notifyAll();
225: }
226:
227: /**
228: * default visibility for tests
229: *
230: */
231: synchronized boolean ownWriteLock(Thread thread) {
232: return writer == thread && writeLocks > 0;
233: }
234:
235: }
|