001: ///////////////////////////////////////////////////////////////////////////////
002: //
003: // Copyright (C) 2003-@year@ by Thomas M. Hazel, MyOODB (www.myoodb.org)
004: //
005: // All Rights Reserved
006: //
007: // This program is free software; you can redistribute it and/or modify
008: // it under the terms of the GNU General Public License and GNU Library
009: // General Public License as published by the Free Software Foundation;
010: // either version 2, or (at your option) any later version.
011: //
012: // This program is distributed in the hope that it will be useful,
013: // but WITHOUT ANY WARRANTY; without even the implied warranty of
014: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: // GNU General Public License and GNU Library General Public License
016: // for more details.
017: //
018: // You should have received a copy of the GNU General Public License
019: // and GNU Library General Public License along with this program; if
020: // not, write to the Free Software Foundation, 675 Mass Ave, Cambridge,
021: // MA 02139, USA.
022: //
023: ///////////////////////////////////////////////////////////////////////////////
024: package org.myoodb.core.storage;
025:
026: import java.io.*;
027:
028: import org.myoodb.util.*;
029: import org.myoodb.core.*;
030:
031: public final class Cluster implements Externalizable, AbstractCluster {
032: private static final org.myoodb.util.Logger LOGGER = org.myoodb.util.Logger
033: .getLogger(Cluster.class);
034:
035: private volatile AbstractLock m_lock;
036: private volatile AbstractObjectContainer m_container;
037: private volatile transient long m_modificationTime = 0;
038:
039: public static File getMostCurrentCluster(String basename,
040: Identifier objectId) {
041: final String strId = objectId.toString();
042: final String strIdWithSuffix = strId
043: + ClusterStore.SUFFIX_SEPARATOR;
044: final String strIdClusterPath = ClusterStore.getDirectoryName(
045: strId, objectId.isRoot());
046:
047: FilenameFilter clusterFilter = new FilenameFilter() {
048: public boolean accept(File dir, String name) {
049: return (name.startsWith(strIdWithSuffix) == true);
050: }
051: };
052:
053: File current = new File(basename + ClusterStore.SUFFIX_CLUSTER);
054: File dfile = new File(strIdClusterPath);
055:
056: final String[] fileList = dfile.list(clusterFilter);
057: if (fileList != null) {
058: final int tokenLen = (ClusterStore.STORE_AS_XML == false) ? 3
059: : 4;
060:
061: long lastTX = -1;
062: for (int i = 0; i < fileList.length; i++) {
063: String[] tokens = fileList[i].split("\\.");
064: if ((tokens.length == tokenLen)
065: && (tokens[2].equals(ClusterStore.CHILD_EXT) == true)) {
066: long tmpTX = Long.parseLong(tokens[1]);
067: if (tmpTX > lastTX) {
068: current = new File(strIdClusterPath
069: + File.separator + fileList[i]);
070: lastTX = tmpTX;
071: }
072: }
073: }
074: }
075:
076: return current;
077: }
078:
079: public static void delete(Identifier id) throws IOException {
080: File file = new File(ClusterStore.getBasename(id)
081: + ClusterStore.SUFFIX_CLUSTER);
082:
083: if (file.exists() == true) {
084: org.myoodb.core.FileHelper.delete(file);
085: }
086:
087: File dfile = new File(ClusterStore.getDirectoryName(id
088: .toString(), id.isRoot()));
089: String[] files = dfile.list();
090: if ((files != null) && (files.length == 0)) {
091: org.myoodb.core.FileHelper.delete(dfile);
092: }
093: }
094:
095: public static void backup(AbstractTransaction tx, String basename,
096: File current) throws IOException {
097: if (current.exists() == true) {
098: File base = new File(basename + ClusterStore.SUFFIX_CLUSTER);
099:
100: if (current.equals(base) == true) {
101: File backup = new File(basename
102: + ClusterStore.SUFFIX_SEPARATOR
103: + tx.getTransactionIdentifier()
104: + ClusterStore.SUFFIX_BACKUP);
105:
106: org.myoodb.core.FileHelper.rename(current, backup);
107: } else {
108: org.myoodb.core.FileHelper.delete(current);
109: }
110: }
111: }
112:
113: public Cluster() {
114: }
115:
116: public Cluster(AbstractObjectContainer container, AbstractLock lock) {
117: m_lock = lock;
118: setContainer(container);
119: }
120:
121: protected void createChild(AbstractTransaction tx)
122: throws IOException {
123: String basename = ClusterStore
124: .getBasename(getObjectIdentifier());
125:
126: File child = new File(basename + ClusterStore.SUFFIX_SEPARATOR
127: + tx.getTransactionIdentifier()
128: + ClusterStore.SUFFIX_CHILD);
129:
130: File current = getMostCurrentCluster(basename,
131: getObjectIdentifier());
132:
133: org.myoodb.core.FileHelper.transfer(current, child);
134: }
135:
136: protected void commitChild(AbstractTransaction tx)
137: throws IOException {
138: String basename = ClusterStore
139: .getBasename(getObjectIdentifier());
140:
141: File temp = new File(basename + ClusterStore.SUFFIX_SEPARATOR
142: + tx.getTransactionIdentifier()
143: + ClusterStore.SUFFIX_TEMP);
144:
145: File child = new File(basename + ClusterStore.SUFFIX_SEPARATOR
146: + tx.getTransactionIdentifier()
147: + ClusterStore.SUFFIX_CHILD);
148:
149: if (temp.exists() == true) {
150: org.myoodb.core.FileHelper.delete(temp);
151: }
152:
153: org.myoodb.core.FileHelper.rename(child, temp);
154:
155: File current = getMostCurrentCluster(basename,
156: getObjectIdentifier());
157: Cluster.backup(tx, basename, current);
158:
159: org.myoodb.core.FileHelper.rename(temp, current);
160: }
161:
162: protected void deleteChild(AbstractTransaction tx)
163: throws IOException {
164: String basename = ClusterStore
165: .getBasename(getObjectIdentifier());
166:
167: File child = new File(basename + ClusterStore.SUFFIX_SEPARATOR
168: + tx.getTransactionIdentifier()
169: + ClusterStore.SUFFIX_CHILD);
170:
171: if (child.exists() == true) {
172: org.myoodb.core.FileHelper.delete(child);
173: }
174: }
175:
176: protected Identifier getObjectIdentifier() {
177: return m_container.getObjectIdentifier();
178: }
179:
180: public AbstractLock getLock() {
181: return m_lock;
182: }
183:
184: public void setLock(AbstractLock lock) {
185: m_lock = lock;
186: }
187:
188: public void setModificationTime(long time) {
189: m_modificationTime = time;
190: }
191:
192: public long getModificationTime() {
193: return m_modificationTime;
194: }
195:
196: private void setContainer(AbstractObjectContainer container) {
197: m_container = container;
198: m_container.setCluster(this );
199: }
200:
201: public AbstractObjectContainer getContainer() {
202: return m_container;
203: }
204:
205: public void prepare(AbstractTransaction tx) throws IOException {
206: if (tx.getTransactionType() == AbstractTransaction.EXPLICIT_TRANSACTION) {
207: createChild(tx);
208: }
209: }
210:
211: public void commit(AbstractTransaction tx) throws IOException {
212: if (tx.getSnapShotFlag() == false) {
213: if (tx.getTransactionType() == AbstractTransaction.EXPLICIT_TRANSACTION) {
214: commitChild(tx);
215: }
216:
217: if (tx.isExplicitPiggyBack() == false) {
218: m_lock.release(tx);
219: }
220: }
221: }
222:
223: public void abort(AbstractTransaction tx) throws IOException {
224: if (tx.getTransactionType() == AbstractTransaction.EXPLICIT_TRANSACTION) {
225: deleteChild(tx);
226: }
227:
228: m_lock.release(tx);
229: }
230:
231: public boolean delete(AbstractTransaction tx) throws IOException {
232: boolean isBase = false;
233:
234: if (tx.getSnapShotFlag() == false) {
235: String basename = ClusterStore
236: .getBasename(getObjectIdentifier());
237: File base = new File(basename + ClusterStore.SUFFIX_CLUSTER);
238:
239: File child = new File(basename
240: + ClusterStore.SUFFIX_SEPARATOR
241: + tx.getTransactionIdentifier()
242: + ClusterStore.SUFFIX_CHILD);
243:
244: if (child.exists() == false) {
245: File current = Cluster.getMostCurrentCluster(basename,
246: getObjectIdentifier());
247:
248: if (current.equals(base) == true) {
249: child = current;
250: }
251: }
252:
253: if (child.equals(base) == true) {
254: isBase = true;
255: }
256:
257: if (child.exists() == true) {
258: org.myoodb.core.FileHelper.delete(child);
259: }
260:
261: File dfile = new File(ClusterStore.getDirectoryName(
262: getObjectIdentifier().toString(),
263: getObjectIdentifier().isRoot()));
264: String[] files = dfile.list();
265: if ((files != null) && (files.length == 0)) {
266: org.myoodb.core.FileHelper.delete(dfile);
267: }
268: }
269:
270: return isBase;
271: }
272:
273: public void read(String filename) throws IOException,
274: ClassNotFoundException {
275: ObjectInput in = null;
276:
277: if (ClusterStore.STORE_AS_XML == true) {
278: com.thoughtworks.xstream.XStream xstream = new com.thoughtworks.xstream.XStream();
279: in = xstream.createObjectInputStream(new InputStreamReader(
280: new BufferedInputStream(new FileInputStream(
281: filename))));
282: //((XStreamInputStream) in).setContext(MyOodbManager.getTheManager().getDatabase());
283: } else {
284: in = new FastObjectInputStream(new BufferedInputStream(
285: new FileInputStream(filename)));
286: ((FastObjectInputStream) in).setContext(MyOodbManager
287: .getTheManager().getDatabase());
288: }
289:
290: try {
291: readExternal(in);
292: } finally {
293: in.close();
294: }
295:
296: m_modificationTime = System.currentTimeMillis();
297: }
298:
299: public void write(File file) throws IOException {
300: java.util.ConcurrentModificationException globalError = null;
301:
302: for (int i = 0; i < ClusterStore.CONCURRENCY_RETRY; i++) {
303: org.myoodb.util.FastByteArrayOutputStream baos = new org.myoodb.util.FastByteArrayOutputStream();
304:
305: try {
306: ObjectOutput out = null;
307:
308: if (ClusterStore.STORE_AS_XML == true) {
309: com.thoughtworks.xstream.XStream xstream = new com.thoughtworks.xstream.XStream();
310: out = xstream
311: .createObjectOutputStream(new OutputStreamWriter(
312: new BufferedOutputStream(baos)));
313: } else {
314: out = new FastObjectOutputStream(
315: new BufferedOutputStream(baos));
316: }
317:
318: try {
319: writeExternal(out);
320: out.flush();
321:
322: RandomAccessFile raFile = new RandomAccessFile(
323: file, FileHelper.getFileWriteMode());
324:
325: try {
326: byte[] buffer = baos.toByteArray();
327: raFile.write(buffer, 0, baos.size());
328: } finally {
329: if (raFile != null) {
330: raFile.close();
331: }
332: }
333:
334: globalError = null;
335: break;
336: } finally {
337: out.close();
338: }
339: } catch (java.util.ConcurrentModificationException e) {
340: if (LOGGER.isDebugEnabled() == true) {
341: LOGGER.debug(null, e);
342: }
343:
344: try {
345: Thread.sleep(100);
346: } catch (java.lang.InterruptedException ee) {
347: // nothing to do
348: }
349:
350: globalError = e;
351: }
352: }
353:
354: if (globalError != null) {
355: throw globalError;
356: }
357:
358: m_modificationTime = System.currentTimeMillis();
359: }
360:
361: public void write(String path) throws IOException {
362: String basename = ClusterStore.getBasename(
363: getObjectIdentifier(), path);
364:
365: File cluster = new File(basename + ClusterStore.SUFFIX_CLUSTER);
366:
367: write(cluster);
368: }
369:
370: public void write() throws IOException {
371: String basename = ClusterStore
372: .getBasename(getObjectIdentifier());
373:
374: File cluster = new File(basename + ClusterStore.SUFFIX_CLUSTER);
375:
376: write(cluster);
377: }
378:
379: public synchronized void writeExternal(ObjectOutput out)
380: throws IOException {
381: out.writeObject(m_lock);
382: out.writeObject(m_container);
383: }
384:
385: public synchronized void readExternal(ObjectInput in)
386: throws IOException, ClassNotFoundException {
387: m_lock = (AbstractLock) in.readObject();
388: setContainer((AbstractObjectContainer) in.readObject());
389: }
390: }
|