KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ozoneDB > core > storage > wizardStore > WizardCluster


1 // You can redistribute this software and/or modify it under the terms of
2
// the Ozone Core License version 1 published by ozone-db.org.
3
//
4
// The original code and portions created by SMB are
5
// Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
6
//
7
// $Id$
8

9 package org.ozoneDB.core.storage.wizardStore;
10
11 import java.io.Externalizable JavaDoc;
12 import java.io.File JavaDoc;
13 import java.io.FileInputStream JavaDoc;
14 import java.io.FileOutputStream JavaDoc;
15 import java.io.IOException JavaDoc;
16 import java.io.InputStream JavaDoc;
17 import java.io.ObjectInput JavaDoc;
18 import java.io.ObjectOutput JavaDoc;
19 import java.io.ObjectStreamException JavaDoc;
20 import java.io.OutputStream JavaDoc;
21 import org.ozoneDB.DxLib.DxArrayBag;
22 import org.ozoneDB.DxLib.DxCollection;
23 import org.ozoneDB.DxLib.DxHashMap;
24 import org.ozoneDB.DxLib.DxIterator;
25 import org.ozoneDB.DxLib.DxLong;
26 import org.ozoneDB.core.Lock;
27 import org.ozoneDB.core.MROWLock;
28 import org.ozoneDB.core.Permissions;
29 import org.ozoneDB.core.Transaction;
30 import org.ozoneDB.core.TransactionID;
31 import org.ozoneDB.core.storage.AbstractCluster;
32 import org.ozoneDB.core.storage.StorageObjectContainer;
33 import org.ozoneDB.core.storage.ClusterID;
34 import org.ozoneDB.util.LogWriter;
35
36
37
38 /**
39  * @author <a HREF="http://www.softwarebuero.de/">SMB</a>
40  * @author <a HREF="http://www.medium.net/">Medium.net</a>
41  * @version $Revision$Date$
42  * TODO: add final modifier when backward compatibility for 1.1 does not have to be maintained
43  */

44 public class WizardCluster extends AbstractCluster implements Externalizable JavaDoc {
45
46     protected final static long serialVersionUID = 2L;
47     protected final static byte subSerialVersionUID = 1;
48
49     protected transient Lock lock;
50
51     protected transient long lastTouched;
52
53     protected transient int bytesPerContainer;
54
55     protected long modTime;
56
57     /**
58      The count of users of this cluster which requested it to be pinned.
59      If a cluster is pinned, it may not be "passivated" (i.e. written to disk and forgotten),
60      it has to stay in memory.
61      This cluster is said to be pinned iff pinCount!=0.
62      Access to this count is only allowed during synchronization on this WizardCluster.
63      */

64     protected transient int pinCount;
65
66
67     /**
68      * Constructor to be used for Externalizable object serialisation.
69      */

70     public WizardCluster() {
71         this(null, null, null, 0);
72     }
73
74
75     public WizardCluster(ClusterID _clusterID, Permissions _permissions, Lock _lock, int _bpc) {
76         // the env and clusterStore will be set by clusterStore
77
clusterID = _clusterID;
78         permissions = _permissions;
79         lock = _lock;
80         bytesPerContainer = _bpc;
81         containers = new DxHashMap(1024);
82         if (lock != null) {
83             ((MROWLock) lock).setDebugInfo("clusterID=" + clusterID);
84         }
85     }
86
87
88 // public void finalize() {
89
// if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
90
// env.logWriter.newEntry(this, "---finalize(): cluster " + clusterID, LogWriter.DEBUG3);
91
// }
92
// }
93

94
95     public long modTime() {
96         return modTime;
97     }
98
99
100     /**
101      * Priority of this cluster to stay in the cluster cache. Low return value
102      * means low priority.
103      * @return Cache priority of the cluster.
104      */

105     public DxLong cachePriority() {
106         return new DxLong(lastTouched);
107     }
108
109
110     public synchronized void setCurrentSize(int byteSize) {
111         int containerCount = containers.count();
112         bytesPerContainer = containerCount > 0 ? byteSize / containerCount : clusterStore.currentBytesPerContainer();
113     }
114
115
116     public int size() {
117         return containers.count() * bytesPerContainer;
118     }
119
120
121     /**
122      * @return True if at least one container is currently invoked.
123      */

124     public boolean isInvoked() {
125         //FIXME: should be a bit in the status byte that is directly changed
126
// by clusters when they are changed
127
StorageObjectContainer container;
128         DxIterator it = containers.iterator();
129         while ((container = (StorageObjectContainer) it.next()) != null) {
130             if (container.isInvoked()) {
131                 return true;
132             }
133         }
134         return false;
135     }
136
137
138     public synchronized void touch() {
139         lastTouched = clusterStore.touchCount++;
140     }
141
142
143     public void registerContainer(StorageObjectContainer container) {
144         synchronized (container) {
145             container.setCluster(this);
146             if (containers.addForKey(container, container.id()) == false) {
147                 throw new IllegalStateException JavaDoc("Unable to add id " + container.id() + " to cluster " + clusterID());
148             }
149         }
150     }
151
152
153     public void removeContainer(StorageObjectContainer container) {
154         if (containers.removeForKey(container.id()) == null) {
155             throw new IllegalStateException JavaDoc("Unable to remove container from cluster.");
156         }
157     }
158
159
160     /**
161      * Note: This method must not be synchronized.
162      *
163      * @param ta
164      */

165     public void updateLockLevel(Transaction ta) throws IOException JavaDoc {
166         if (env.logWriter.hasTarget(LogWriter.DEBUG2)) {
167             env.logWriter.newEntry(this, "updateLockLevel(): " + clusterID, LogWriter.DEBUG2);
168             env.logWriter.newEntry(this, " lock: " + lock.level(ta), LogWriter.DEBUG2);
169         }
170         if (lock.level(ta) > Lock.LEVEL_UPGRADE) {
171             saveShadow();
172         }
173     }
174
175
176     public void prepareCommit(Transaction ta) /*throws Exception*/ {
177         if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
178             env.logWriter.newEntry(this, "prepareCommit()" + clusterID, LogWriter.DEBUG3);
179         }
180         if (lock.level(ta) > Lock.LEVEL_UPGRADE) {
181             synchronized (this) {
182                 modTime = System.currentTimeMillis();
183             }
184         }
185     }
186
187
188     public void commit(Transaction ta) throws IOException JavaDoc {
189         if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
190             env.logWriter.newEntry(this, "commit()" + clusterID, LogWriter.DEBUG3);
191         }
192
193         if (true || lock != null) {
194             if (lock.level(ta) > Lock.LEVEL_UPGRADE) {
195                 deleteShadow();
196             }
197             lock.release(ta);
198         } else {
199 // env.logWriter.newEntry( this, this+".commit(): lock="+lock+".", LogWriter.DEBUG3 );
200
throw new NullPointerException JavaDoc(this + ".commit(" + ta + "): lock=" + lock + ".");
201         }
202     }
203
204
205     public void abort(Transaction ta) throws IOException JavaDoc {
206         if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
207             env.logWriter.newEntry(this, "abort()" + clusterID, LogWriter.DEBUG3);
208         }
209
210         if (true || lock != null) {
211             if (lock.level(ta) > Lock.LEVEL_UPGRADE) {
212                 restoreShadow();
213                 deleteShadow();
214             }
215             lock.release(ta);
216         } else {
217             throw new NullPointerException JavaDoc(this + ".abort(" + ta + "): lock=" + lock + ".");
218         }
219     }
220
221
222     // protected void clearContainerStates() {
223
// WizardObjectContainer container;
224
// DxIterator it = containers.iterator();
225
// while ((container=(WizardObjectContainer)it.next()) != null) {
226
// container.clearState();
227
// }
228
// }
229

230
231     public DxCollection allLockers() {
232         DxCollection lockerIDs = lock.lockerIDs();
233
234         DxArrayBag result = new DxArrayBag(lockerIDs.count());
235         DxIterator it = lockerIDs.iterator();
236         while (it.next() != null) {
237             result.add(env.transactionManager.taForID((TransactionID) it.object()));
238         }
239
240         return result;
241     }
242
243
244     /**
245         Shadow files are contain the data of original cluster files. If the transaction involving the cluster
246         fails, the cluster file data can be restored because it resides in the shadow file.
247     */

248     protected void saveShadow() throws IOException JavaDoc {
249         if (env != null) {
250 // env.logWriter.newEntry( this, "saveShadow(): " + clusterID, LogWriter.DEBUG3 );
251
}
252
253         String JavaDoc basename = clusterStore.basename(clusterID);
254         File JavaDoc orig = new File JavaDoc(basename + ClusterStore.POSTFIX_CLUSTER);
255         File JavaDoc shadow = new File JavaDoc(basename + ClusterStore.POSTFIX_SHADOW);
256
257         // earlier versions did just _move_ the file; this was faster and caused
258
// no problems because a abnormal server exit did force a recovery in any
259
// case; this behaviour has changed and so we have to copy the file now
260
// in order to keep an intact cluster
261

262         // orig.renameTo( shadow );
263

264         // Why aren't we renaming here? This seems to be required. See the comments of ClusterStore.restoreCluster().
265
// Maybe we should rename _and_ copy from the shadow to the new cluster, not vice versa?
266

267         InputStream JavaDoc in = null;
268         OutputStream JavaDoc out = null;
269         try {
270             in = new FileInputStream JavaDoc(orig);
271             out = new FileOutputStream JavaDoc(shadow);
272
273             final int chunkSize = 4096;
274             byte[] chunk = new byte[chunkSize];
275             int c = 0;
276             while ((c = in.read(chunk)) != -1) {
277                 out.write(chunk, 0, c);
278             }
279         } finally {
280             if (in != null) {
281                 in.close();
282             }
283             if (out != null) {
284                 out.close();
285             }
286         }
287     }
288
289
290     /**
291      * Restore the saved shadow on disk. The content of the receiver stays the
292      * same. The cluster needs to be re-loaded to reflect the changes.
293      */

294     protected void restoreShadow() throws IOException JavaDoc {
295         if (env != null) {
296 // env.logWriter.newEntry( this, "restoreShadow(): " + clusterID, LogWriter.DEBUG3 );
297
}
298
299         // restore cluster on disk by renaming back shadow file
300
String JavaDoc basename = clusterStore.basename(clusterID);
301         File JavaDoc orig = new File JavaDoc(basename + ClusterStore.POSTFIX_CLUSTER);
302         File JavaDoc shadow = new File JavaDoc(basename + ClusterStore.POSTFIX_SHADOW);
303
304         // on Win renaming without prior deleting the target does not work;
305
// long live WORA !
306
orig.delete();
307         if (!shadow.renameTo(orig)) {
308             throw new IOException JavaDoc("Unable to rename shadow file.");
309         }
310     }
311
312
313     protected void deleteShadow() throws IOException JavaDoc {
314         if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
315             env.logWriter.newEntry(this, "deleteShadow(): " + clusterID, LogWriter.DEBUG3);
316         }
317
318         String JavaDoc basename = clusterStore.basename(clusterID);
319         File JavaDoc shadow = new File JavaDoc(basename + ClusterStore.POSTFIX_SHADOW);
320         if (shadow.exists() && !shadow.delete()) {
321             throw new IOException JavaDoc("Unable to delete shadow file.");
322         }
323     }
324
325
326     /**
327      * Delete this cluster from the disk.
328      */

329     public void delete() throws Exception JavaDoc {
330         String JavaDoc basename = clusterStore.basename(clusterID);
331         new File JavaDoc(basename + ClusterStore.POSTFIX_CLUSTER).delete();
332         new File JavaDoc(basename + ClusterStore.POSTFIX_SHADOW).delete();
333         new File JavaDoc(basename + ClusterStore.POSTFIX_TEMP).delete();
334         new File JavaDoc(basename + ClusterStore.POSTFIX_LOCK).delete();
335     }
336
337     public String JavaDoc toString() {
338         return "WizardCluster[id=" + clusterID + "]";
339     }
340
341     public Lock lock() {
342         return lock;
343     }
344
345     public void setLock(Lock to) {
346         //TODO: When can the lock be null and what is the significance of that?
347
if (to == null) {
348             if (env.logWriter.hasTarget(LogWriter.DEBUG)) {
349                 env.logWriter.newEntry(this, this + ".setLock(" + to + ") (oldLock=" + this.lock + "), isPinned()=" + isPinned() + ".", LogWriter.DEBUG);
350             }
351         }
352
353         this.lock = to;
354     }
355
356     public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc {
357 // // cannot use logwriter because of nullpointer exception
358
// if ((containers.count() == 0 && pinCount != 0) || (containers.count() > 0 && pinCount > 1)) {
359
// System.out.println("pincount for " + this + " not 1 while writing (" + pinCount + " for " + containers.count() + " containers)");
360
// }
361
// System.out.println ("cluster.writeExternal()...");
362
out.writeByte(subSerialVersionUID);
363         out.writeObject(clusterID);
364         out.writeObject(permissions);
365         out.writeLong(modTime);
366         // out.writeObject (lock);
367

368         out.writeInt(containers.count());
369         DxIterator it = containers.iterator();
370         StorageObjectContainer container;
371         while ((container = (StorageObjectContainer) it.next()) != null) {
372             // out.writeObject( container );
373
container.writeExternal(out);
374         }
375     }
376
377
378     public void readExternal(ObjectInput JavaDoc in) throws IOException JavaDoc, ObjectStreamException JavaDoc, ClassNotFoundException JavaDoc {
379         // System.out.println ("cluster.readExternal()...");
380
byte streamUID = in.readByte();
381         clusterID = (ClusterID) in.readObject();
382         permissions = (Permissions) in.readObject();
383         modTime = in.readLong();
384         // lock = (Lock)in.readObject();
385

386         int count = in.readInt();
387         for (int i = 0; i < count; i++) {
388             try {
389                 // StorageObjectContainer container = (WizardObjectContainer)in.readObject();
390
StorageObjectContainer container = new WizardObjectContainer();
391                 container.readExternal(in);
392                 container.setCluster(this);
393 // Env.currentEnv().getLogWriter().newEntry(this,this+".readExternal(): container.id()="+container.id()+".",LogWriter.DEBUG2);
394
containers.addForKey(container, container.id());
395             } catch (ObjectStreamException JavaDoc e) {
396                 if (env != null) {
397                     env.logWriter.newEntry(this, "ObjectStreamException for cluster " + clusterID + " at container #" + i + ": ", e, LogWriter.ERROR);
398                 } else {
399                     System.out.println("ObjectStreamException for cluster " + clusterID + " at container #" + i + ": " + e);
400                 }
401                 throw e;
402             }
403         }
404     }
405
406     /**
407      Pins this WizardCluster.
408      Every caller of this method must pair this call with a call to {@link #unpin}.
409      A WizardCluster remains in main memory at least as long as it is pinned.
410      */

411     public synchronized void pin() {
412         int maxPinCount = (1 + containers.count()) * 1000;
413         if (++pinCount > maxPinCount) {
414             env.logWriter.newEntry(this, "pincount for " + this + " getting too large (" + pinCount + " for " + containers.count() + " containers)", LogWriter.ERROR);
415         }
416     }
417
418     /**
419      Unpins this WizardCluster.
420      This method must be called exactly once for every call to {@link #pin}.
421      */

422     public synchronized void unpin() {
423         if (--pinCount < 0) {
424             env.logWriter.newEntry(this, "pincount < 0 (" + pinCount + ") for " + this, LogWriter.WARN);
425             clearPinCount();
426             // something is wrong with the pinning implementation if this happens so we need to signal this
427
// but throwing an exception is a bit too strong
428
// throw new OzoneInternalException("pincount < 0 for " + this);
429
}
430
431     }
432
433     /**
434      Returns wether this cluster is pinned.
435      */

436     public synchronized boolean isPinned() {
437         return pinCount != 0;
438     }
439
440     /**
441      Sets the pin count to zero and returns the former pin count.
442      */

443     public synchronized int clearPinCount() {
444         int oldPinCount = pinCount;
445
446         pinCount = 0;
447
448         return oldPinCount;
449     }
450
451     /**
452      Adds an amount to the pin count
453      */

454     public synchronized void addPinCount(int what) {
455         pinCount += what;
456     }
457 }
458
Popular Tags