001: /*
002: * This file or a portion of this file is licensed under the terms of
003: * the Globus Toolkit Public License, found in file GTPL, or at
004: * http://www.globus.org/toolkit/download/license.html. This notice must
005: * appear in redistributions of this file, with or without modification.
006: *
007: * Redistributions of this Software, with or without modification, must
008: * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
009: * some other similar material which is provided with the Software (if
010: * any).
011: *
012: * Copyright 1999-2004 University of Chicago and The University of
013: * Southern California. All rights reserved.
014: */
015:
016: package org.griphyn.vdl.util;
017:
018: import org.griphyn.vdl.util.*;
019: import java.util.*;
020: import java.io.*;
021:
022: /**
023: * This class implements file locking using system-provided lock
024: * functions. On unix, these may most likely include POSIX fcntl locking
025: * calls. While such lock may on some systems be NFS-safe, networked
026: * file locking on Linux is notoriously broken, and cannot be assumed to
027: * work as expected.<p>
028: *
029: * All access to the files must go through the respective open and close
030: * methods provided by this class in order to guarantee proper locking!
031: *
032: * @author Jens-S. Vöckler
033: * @author Yong Zhao
034: * @version $Revision: 50 $
035: *
036: * @see FcntlLock
037: * @see java.io.File
038: */
039: public class FcntlFileLock extends FileHelper {
040: /**
041: * File channel lock of database.
042: */
043: private FcntlLock m_lock_db;
044:
045: /**
046: * File channel lock of nr file.
047: */
048: private FcntlLock m_lock_nr;
049:
050: /**
051: * Number memory for updates on close.
052: */
053: private int m_current;
054:
055: /**
056: * Remembers, if this instance is already in use.
057: */
058: private boolean m_active;
059:
060: /**
061: * Primary ctor: obtain access to a database cycle via database.
062: * @param database is the base name of the database without the
063: * digit suffix.
064: */
065: public FcntlFileLock(String database) {
066: super (database);
067: this .m_lock_db = this .m_lock_nr = null;
068: this .m_current = -1;
069: this .m_active = false;
070: }
071:
072: /**
073: * Opens a reader for the database, adjusting the database cycles.
074: * The access can be shared with other simultaneous readers. The
075: * locks on number file and database are held shared. They are
076: * released in the close operation.
077: *
078: * @return a reader opened to the database, or null for failure.
079: * @see #closeReader( File )
080: */
081: public synchronized File openReader() {
082: File result = null;
083:
084: // sanity check -- don't reopen without close
085: if (m_active)
086: return result;
087:
088: // lock number file shared
089: // read contents of number file, if available
090: int number = -1;
091: if (m_number.exists()) {
092: try {
093: m_lock_nr = new FcntlLock(m_number, false, true);
094: } catch (IOException e) {
095: Logging.instance().log(
096: "lock",
097: 0,
098: "while creating lock on " + m_number.getPath()
099: + ": " + e.getMessage());
100: return result;
101: }
102:
103: // read number from number file
104: number = readCount();
105:
106: } else {
107: // if the number file does not exit, DO NOT create it (yet)
108: Logging.instance().log(
109: "lock",
110: 2,
111: "number file " + m_number.getPath()
112: + " does not exist, ignoring lock");
113: }
114:
115: // remember number
116: m_current = number;
117:
118: // database file
119: File database = new File(number == -1 ? m_database : m_database
120: + '.' + Integer.toString(number));
121:
122: // lock and open database
123: if (database.exists()) {
124: try {
125: m_lock_db = new FcntlLock(database, false, true);
126: } catch (IOException e) {
127: Logging.instance().log(
128: "lock",
129: 0,
130: "while creating lock on " + database.getPath()
131: + ": " + e.getMessage());
132: return result;
133: }
134: } else {
135: Logging.instance().log(
136: "lock",
137: 1,
138: "database file " + database.getPath()
139: + " does not exist, ignoring");
140: }
141:
142: result = database;
143:
144: // exit condition: Both, number file and database are locked using
145: // a shared (read) lock.
146: m_active = (result != null);
147: return result;
148: }
149:
150: /**
151: * Opens a writer for the database, adjusting the database cycles
152: * The access is exclusive, and cannot be shared with readers nor
153: * writers. Both locks are exclusive, and held through the close
154: * operation.
155: *
156: * @return a writer opened for the database, or null for failure.
157: * @see #closeWriter( File )
158: */
159: public synchronized File openWriter() {
160: File result = null;
161:
162: // sanity check -- don't reopen without close
163: if (m_active)
164: return result;
165:
166: // lock number file exclusively
167: // read contents of number file, if available
168: int number = -1;
169: if (m_number.exists()) {
170: try {
171: m_lock_nr = new FcntlLock(m_number, true, false);
172: } catch (IOException e) {
173: Logging.instance().log(
174: "lock",
175: 0,
176: "while creating lock on " + m_number.getPath()
177: + ": " + e.getMessage());
178: return result;
179: }
180:
181: // read number from number file
182: number = readCount();
183:
184: } else {
185: // if the number file does not exit, DO NOT create it (yet)
186: Logging.instance().log(
187: "lock",
188: 2,
189: "number lock " + m_number.getPath()
190: + " does not exist, ignoring");
191: }
192:
193: // generate new file number to write to
194: m_current = number = (number + 1) % 10;
195:
196: // generate database filename
197: File database = new File(m_database + '.'
198: + Integer.toString(number));
199:
200: // lock and open database for exclusive access
201: try {
202: m_lock_db = new FcntlLock(database, true, false);
203: } catch (IOException e) {
204: Logging.instance().log(
205: "lock",
206: 0,
207: "while creating lock on " + database.getPath()
208: + ": " + e.getMessage());
209: return result;
210: }
211:
212: // valid result
213: result = database;
214: m_active = true;
215:
216: // exit condition: Both, database and number file are locked, using
217: // an exclusive lock.
218: return result;
219: }
220:
221: /**
222: * Closes a previously obtained reader, and releases internal locking
223: * resources.
224: *
225: * @param r is the reader that was created by its open operation.
226: * @return true, if unlocking went smoothly, or false in the presence
227: * of an error.
228: * @see #openReader()
229: */
230: public synchronized boolean closeReader(File r) {
231: boolean result = false;
232:
233: // sanity check
234: if (!m_active)
235: return false;
236: result = true;
237:
238: // done with database
239: if (m_lock_db != null)
240: m_lock_db.done();
241:
242: // done with nr file; however, it may not have existed
243: if (m_lock_nr != null)
244: m_lock_nr.done();
245:
246: m_active = false;
247: return result;
248: }
249:
250: /**
251: * Closes a previously obtained writer, and releases internal
252: * locking resources.
253: *
254: * @param w is the instance that was returned by its open operation.
255: * @return true, if the closing went smoothly, false in the presence
256: * of an error.
257: * @see #openWriter()
258: */
259: public synchronized boolean closeWriter(File w) {
260: boolean result = false;
261:
262: // sanity check
263: if (!m_active)
264: return result;
265:
266: // done with database
267: m_lock_db.done();
268:
269: // update number file
270: result = writeCount(m_current);
271:
272: // release shared lock on number file
273: if (m_lock_nr != null)
274: m_lock_nr.done();
275:
276: m_active = false;
277: return result;
278: }
279: }
|