001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.internal;
022:
023: import com.db4o.*;
024: import com.db4o.foundation.*;
025: import com.db4o.internal.callbacks.*;
026: import com.db4o.internal.cs.*;
027: import com.db4o.internal.freespace.*;
028: import com.db4o.internal.marshall.*;
029: import com.db4o.internal.slots.*;
030:
031: /**
032: * @exclude
033: */
034: public class LocalTransaction extends Transaction {
035:
036: private final byte[] _pointerBuffer = new byte[Const4.POINTER_LENGTH];
037:
038: protected final StatefulBuffer i_pointerIo;
039:
040: private int i_address; // only used to pass address to Thread
041:
042: private final Collection4 _participants = new Collection4();
043:
044: private final LockedTree _slotChanges = new LockedTree();
045:
046: private Tree _writtenUpdateDeletedMembers;
047:
048: protected final LocalObjectContainer _file;
049:
050: public LocalTransaction(ObjectContainerBase container,
051: Transaction parentTransaction,
052: TransactionalReferenceSystem referenceSystem) {
053: super (container, parentTransaction, referenceSystem);
054: _file = (LocalObjectContainer) container;
055: i_pointerIo = new StatefulBuffer(this , Const4.POINTER_LENGTH);
056: }
057:
058: public LocalObjectContainer file() {
059: return _file;
060: }
061:
062: public void commit() {
063: commit(null);
064: }
065:
066: public void commit(ServerMessageDispatcher dispatcher) {
067: synchronized (container()._lock) {
068: if (doCommittingCallbacks()) {
069: callbacks().commitOnStarted(this ,
070: collectCallbackObjectInfos(dispatcher));
071: }
072: freespaceBeginCommit();
073: commitImpl();
074: CallbackObjectInfoCollections committedInfo = null;
075: if (doCommittedCallbacks(dispatcher)) {
076: committedInfo = collectCallbackObjectInfos(dispatcher);
077: }
078: commitClearAll();
079: freespaceEndCommit();
080: if (doCommittedCallbacks(dispatcher)) {
081: if (dispatcher == null) {
082: callbacks().commitOnCompleted(this , committedInfo);
083: } else {
084: dispatcher.committedInfo(committedInfo);
085: }
086: }
087: }
088: }
089:
090: private boolean doCommittedCallbacks(
091: ServerMessageDispatcher dispatcher) {
092: if (isSystemTransaction()) {
093: return false;
094: }
095: if (dispatcher != null) {
096: return dispatcher.server().caresAboutCommitted();
097: }
098: return callbacks().caresAboutCommitted();
099: }
100:
101: private boolean doCommittingCallbacks() {
102: return !isSystemTransaction()
103: && callbacks().caresAboutCommitting();
104: }
105:
106: public void enlist(TransactionParticipant participant) {
107: if (null == participant) {
108: throw new ArgumentNullException();
109: }
110: checkSynchronization();
111: if (!_participants.containsByIdentity(participant)) {
112: _participants.add(participant);
113: }
114: }
115:
116: private void commitImpl() {
117:
118: if (DTrace.enabled) {
119: DTrace.TRANS_COMMIT.logInfo("server == "
120: + container().isServer() + ", systemtrans == "
121: + isSystemTransaction());
122: }
123:
124: commit2Listeners();
125:
126: commit3Stream();
127:
128: commit4FieldIndexes();
129:
130: commitParticipants();
131:
132: container().writeDirty();
133:
134: Slot reservedSlot = allocateTransactionLogSlot(false);
135:
136: freeSlotChanges(false);
137:
138: commitFreespace();
139:
140: freeSlotChanges(true);
141:
142: commit6WriteChanges(reservedSlot);
143: }
144:
145: private final void freeSlotChanges(final boolean forFreespace) {
146: Visitor4 visitor = new Visitor4() {
147: public void visit(Object obj) {
148: ((SlotChange) obj)
149: .freeDuringCommit(_file, forFreespace);
150: }
151: };
152: if (isSystemTransaction()) {
153: _slotChanges.traverseMutable(visitor);
154: return;
155: }
156: _slotChanges.traverseLocked(visitor);
157: if (_systemTransaction != null) {
158: parentLocalTransaction().freeSlotChanges(forFreespace);
159: }
160: }
161:
162: private void commit2Listeners() {
163: commitParentListeners();
164: commitTransactionListeners();
165: }
166:
167: private void commitParentListeners() {
168: if (_systemTransaction != null) {
169: parentLocalTransaction().commit2Listeners();
170: }
171: }
172:
173: private void commitParticipants() {
174: if (parentLocalTransaction() != null) {
175: parentLocalTransaction().commitParticipants();
176: }
177:
178: Iterator4 iterator = _participants.iterator();
179: while (iterator.moveNext()) {
180: ((TransactionParticipant) iterator.current()).commit(this );
181: }
182: }
183:
184: private void commit3Stream() {
185: container().processPendingClassUpdates();
186: container().writeDirty();
187: container().classCollection().write(
188: container().systemTransaction());
189: }
190:
191: private LocalTransaction parentLocalTransaction() {
192: return (LocalTransaction) _systemTransaction;
193: }
194:
195: private void commitClearAll() {
196: if (_systemTransaction != null) {
197: parentLocalTransaction().commitClearAll();
198: }
199: clearAll();
200: }
201:
202: protected void clear() {
203: _slotChanges.clear();
204: disposeParticipants();
205: _participants.clear();
206: }
207:
208: private void disposeParticipants() {
209: Iterator4 iterator = _participants.iterator();
210: while (iterator.moveNext()) {
211: ((TransactionParticipant) iterator.current()).dispose(this );
212: }
213: }
214:
215: public void rollback() {
216: synchronized (container()._lock) {
217:
218: rollbackParticipants();
219:
220: rollbackFieldIndexes();
221:
222: rollbackSlotChanges();
223:
224: rollBackTransactionListeners();
225:
226: clearAll();
227: }
228: }
229:
230: private void rollbackParticipants() {
231: Iterator4 iterator = _participants.iterator();
232: while (iterator.moveNext()) {
233: ((TransactionParticipant) iterator.current())
234: .rollback(this );
235: }
236: }
237:
238: protected void rollbackSlotChanges() {
239: _slotChanges.traverseLocked(new Visitor4() {
240: public void visit(Object a_object) {
241: ((SlotChange) a_object).rollback(_file);
242: }
243: });
244: }
245:
246: public boolean isDeleted(int id) {
247: return slotChangeIsFlaggedDeleted(id);
248: }
249:
250: private Slot allocateTransactionLogSlot(boolean appendToFile) {
251: int transactionLogByteCount = transactionLogSlotLength();
252: if (freespaceManager() != null) {
253: int blockedLength = _file
254: .bytesToBlocks(transactionLogByteCount);
255: Slot slot = freespaceManager().allocateTransactionLogSlot(
256: blockedLength);
257: if (slot != null) {
258: return _file.toNonBlockedLength(slot);
259: }
260: }
261: if (!appendToFile) {
262: return null;
263: }
264: return _file.appendBytes(transactionLogByteCount);
265: }
266:
267: private int transactionLogSlotLength() {
268: // slotchanges * 3 for ID, address, length
269: // 2 ints for slotlength and count
270: return ((countSlotChanges() * 3) + 2) * Const4.INT_LENGTH;
271: }
272:
273: private boolean slotLongEnoughForLog(Slot slot) {
274: return slot != null
275: && slot.length() >= transactionLogSlotLength();
276: }
277:
278: protected final void commit6WriteChanges(Slot reservedSlot) {
279: checkSynchronization();
280:
281: int slotChangeCount = countSlotChanges();
282:
283: if (slotChangeCount > 0) {
284:
285: Slot transactionLogSlot = slotLongEnoughForLog(reservedSlot) ? reservedSlot
286: : allocateTransactionLogSlot(true);
287:
288: final StatefulBuffer buffer = new StatefulBuffer(this ,
289: transactionLogSlot);
290: buffer.writeInt(transactionLogSlot.length());
291: buffer.writeInt(slotChangeCount);
292:
293: appendSlotChanges(buffer);
294:
295: buffer.write();
296: flushFile();
297:
298: container().writeTransactionPointer(
299: transactionLogSlot.address());
300: flushFile();
301:
302: if (writeSlots()) {
303: flushFile();
304: }
305:
306: container().writeTransactionPointer(0);
307: flushFile();
308:
309: if (transactionLogSlot != reservedSlot) {
310: freeTransactionLogSlot(transactionLogSlot);
311: }
312: }
313: freeTransactionLogSlot(reservedSlot);
314: }
315:
316: private void freeTransactionLogSlot(Slot slot) {
317: if (slot == null) {
318: return;
319: }
320: if (freespaceManager() == null) {
321: return;
322: }
323: freespaceManager().freeTransactionLogSlot(
324: _file.toBlockedLength(slot));
325: }
326:
327: public void writeZeroPointer(int id) {
328: writePointer(id, Slot.ZERO);
329: }
330:
331: public void writePointer(Pointer4 pointer) {
332: writePointer(pointer._id, pointer._slot);
333: }
334:
335: public void writePointer(int id, Slot slot) {
336: if (DTrace.enabled) {
337: DTrace.WRITE_POINTER.log(id);
338: DTrace.WRITE_POINTER.logLength(slot);
339: }
340: checkSynchronization();
341: i_pointerIo.useSlot(id);
342: if (Deploy.debug) {
343: i_pointerIo.writeBegin(Const4.YAPPOINTER);
344: }
345: i_pointerIo.writeSlot(slot);
346: if (Deploy.debug) {
347: i_pointerIo.writeEnd();
348: }
349: if (Debug.xbytes && Deploy.overwrite) {
350: i_pointerIo.setID(Const4.IGNORE_ID);
351: }
352: i_pointerIo.write();
353: }
354:
355: private boolean writeSlots() {
356: final BooleanByRef ret = new BooleanByRef();
357: traverseSlotChanges(new Visitor4() {
358: public void visit(Object obj) {
359: ((SlotChange) obj).writePointer(LocalTransaction.this );
360: ret.value = true;
361: }
362: });
363: return ret.value;
364: }
365:
366: public void flushFile() {
367: if (DTrace.enabled) {
368: DTrace.TRANS_FLUSH.log();
369: }
370: if (_file.configImpl().flushFileBuffers()) {
371: _file.syncFiles();
372: }
373: }
374:
375: private SlotChange produceSlotChange(int id) {
376: if (DTrace.enabled) {
377: DTrace.PRODUCE_SLOT_CHANGE.log(id);
378: }
379: SlotChange slot = new SlotChange(id);
380: _slotChanges.add(slot);
381: return (SlotChange) slot.addedOrExisting();
382: }
383:
384: private final SlotChange findSlotChange(int a_id) {
385: checkSynchronization();
386: return (SlotChange) _slotChanges.find(a_id);
387: }
388:
389: public Slot getCurrentSlotOfID(int id) {
390: checkSynchronization();
391: if (id == 0) {
392: return null;
393: }
394: SlotChange change = findSlotChange(id);
395: if (change != null) {
396: if (change.isSetPointer()) {
397: return change.newSlot();
398: }
399: }
400:
401: if (_systemTransaction != null) {
402: Slot parentSlot = parentLocalTransaction()
403: .getCurrentSlotOfID(id);
404: if (parentSlot != null) {
405: return parentSlot;
406: }
407: }
408: return readPointer(id)._slot;
409: }
410:
411: public Slot getCommittedSlotOfID(int id) {
412: if (id == 0) {
413: return null;
414: }
415: SlotChange change = findSlotChange(id);
416: if (change != null) {
417: Slot slot = change.oldSlot();
418: if (slot != null) {
419: return slot;
420: }
421: }
422:
423: if (_systemTransaction != null) {
424: Slot parentSlot = parentLocalTransaction()
425: .getCommittedSlotOfID(id);
426: if (parentSlot != null) {
427: return parentSlot;
428: }
429: }
430: return readPointer(id)._slot;
431: }
432:
433: public Pointer4 readPointer(int id) {
434: if (Deploy.debug) {
435: return debugReadPointer(id);
436: }
437: _file.readBytes(_pointerBuffer, id, Const4.POINTER_LENGTH);
438: int address = (_pointerBuffer[3] & 255)
439: | (_pointerBuffer[2] & 255) << 8
440: | (_pointerBuffer[1] & 255) << 16
441: | _pointerBuffer[0] << 24;
442: int length = (_pointerBuffer[7] & 255)
443: | (_pointerBuffer[6] & 255) << 8
444: | (_pointerBuffer[5] & 255) << 16
445: | _pointerBuffer[4] << 24;
446: return new Pointer4(id, new Slot(address, length));
447: }
448:
449: private Pointer4 debugReadPointer(int id) {
450: if (Deploy.debug) {
451: i_pointerIo.useSlot(id);
452: i_pointerIo.read();
453: i_pointerIo.readBegin(Const4.YAPPOINTER);
454: int debugAddress = i_pointerIo.readInt();
455: int debugLength = i_pointerIo.readInt();
456: i_pointerIo.readEnd();
457: return new Pointer4(id, new Slot(debugAddress, debugLength));
458: }
459: return null;
460: }
461:
462: public void setPointer(int a_id, Slot slot) {
463: if (DTrace.enabled) {
464: DTrace.SLOT_SET_POINTER.log(a_id);
465: DTrace.SLOT_SET_POINTER.logLength(slot);
466: }
467: checkSynchronization();
468: produceSlotChange(a_id).setPointer(slot);
469: }
470:
471: private boolean slotChangeIsFlaggedDeleted(int id) {
472: SlotChange slot = findSlotChange(id);
473: if (slot != null) {
474: return slot.isDeleted();
475: }
476: if (_systemTransaction != null) {
477: return parentLocalTransaction().slotChangeIsFlaggedDeleted(
478: id);
479: }
480: return false;
481: }
482:
483: private int countSlotChanges() {
484: final IntByRef count = new IntByRef();
485: traverseSlotChanges(new Visitor4() {
486: public void visit(Object obj) {
487: SlotChange slot = (SlotChange) obj;
488: if (slot.isSetPointer()) {
489: count.value++;
490: }
491: }
492: });
493: return count.value;
494: }
495:
496: final void writeOld() {
497: synchronized (container()._lock) {
498: i_pointerIo.useSlot(i_address);
499: i_pointerIo.read();
500: int length = i_pointerIo.readInt();
501: if (length > 0) {
502: StatefulBuffer bytes = new StatefulBuffer(this ,
503: i_address, length);
504: bytes.read();
505: bytes.incrementOffset(Const4.INT_LENGTH);
506: _slotChanges.read(bytes, new SlotChange(0));
507: if (writeSlots()) {
508: flushFile();
509: }
510: container().writeTransactionPointer(0);
511: flushFile();
512: freeSlotChanges(false);
513: } else {
514: container().writeTransactionPointer(0);
515: flushFile();
516: }
517: }
518: }
519:
520: private void appendSlotChanges(final Buffer writer) {
521: traverseSlotChanges(new Visitor4() {
522: public void visit(Object obj) {
523: ((SlotChange) obj).write(writer);
524: }
525: });
526: }
527:
528: private void traverseSlotChanges(Visitor4 visitor) {
529: if (_systemTransaction != null) {
530: parentLocalTransaction().traverseSlotChanges(visitor);
531: }
532: _slotChanges.traverseLocked(visitor);
533: }
534:
535: public void slotDelete(int id, Slot slot) {
536: checkSynchronization();
537: if (DTrace.enabled) {
538: DTrace.SLOT_DELETE.log(id);
539: DTrace.SLOT_DELETE.logLength(slot);
540: }
541: if (id == 0) {
542: return;
543: }
544: SlotChange slotChange = produceSlotChange(id);
545: slotChange.freeOnCommit(_file, slot);
546: slotChange.setPointer(Slot.ZERO);
547: }
548:
549: public void slotFreeOnCommit(int id, Slot slot) {
550: checkSynchronization();
551: if (DTrace.enabled) {
552: DTrace.SLOT_FREE_ON_COMMIT.log(id);
553: DTrace.SLOT_FREE_ON_COMMIT.logLength(slot);
554: }
555: if (id == 0) {
556: return;
557: }
558: produceSlotChange(id).freeOnCommit(_file, slot);
559: }
560:
561: public void slotFreeOnRollback(int id, Slot slot) {
562: checkSynchronization();
563: if (DTrace.enabled) {
564: DTrace.SLOT_FREE_ON_ROLLBACK_ID.log(id);
565: DTrace.SLOT_FREE_ON_ROLLBACK_ADDRESS.logLength(slot);
566: }
567: produceSlotChange(id).freeOnRollback(slot);
568: }
569:
570: void slotFreeOnRollbackCommitSetPointer(int id, Slot newSlot,
571: boolean forFreespace) {
572:
573: Slot oldSlot = getCurrentSlotOfID(id);
574: if (oldSlot == null) {
575: return;
576: }
577:
578: checkSynchronization();
579:
580: if (DTrace.enabled) {
581: DTrace.FREE_ON_ROLLBACK.log(id);
582: DTrace.FREE_ON_ROLLBACK.logLength(newSlot);
583: DTrace.FREE_ON_COMMIT.log(id);
584: DTrace.FREE_ON_COMMIT.logLength(oldSlot);
585: }
586:
587: SlotChange change = produceSlotChange(id);
588: change.freeOnRollbackSetPointer(newSlot);
589: change.freeOnCommit(_file, oldSlot);
590: change.forFreespace(forFreespace);
591: }
592:
593: void produceUpdateSlotChange(int id, Slot slot) {
594: checkSynchronization();
595: if (DTrace.enabled) {
596: DTrace.FREE_ON_ROLLBACK.log(id);
597: DTrace.FREE_ON_ROLLBACK.logLength(slot);
598: }
599:
600: final SlotChange slotChange = produceSlotChange(id);
601: slotChange.freeOnRollbackSetPointer(slot);
602: }
603:
604: public void slotFreePointerOnCommit(int a_id) {
605: checkSynchronization();
606: Slot slot = getCurrentSlotOfID(a_id);
607: if (slot == null) {
608: return;
609: }
610:
611: // FIXME: From looking at this it should call slotFreePointerOnCommit
612: // Write a test case and check.
613:
614: // Looking at references, this method is only called from freed
615: // BTree nodes. Indeed it should be checked what happens here.
616:
617: slotFreeOnCommit(a_id, slot);
618: }
619:
620: void slotFreePointerOnCommit(int a_id, Slot slot) {
621: checkSynchronization();
622: slotFreeOnCommit(slot.address(), slot);
623:
624: // FIXME: This does not look nice
625: slotFreeOnCommit(a_id, slot);
626:
627: // FIXME: It should rather work like this:
628: // produceSlotChange(a_id).freePointerOnCommit();
629: }
630:
631: public void slotFreePointerOnRollback(int id) {
632: produceSlotChange(id).freePointerOnRollback();
633: }
634:
635: public void processDeletes() {
636: if (_delete == null) {
637: _writtenUpdateDeletedMembers = null;
638: return;
639: }
640:
641: while (_delete != null) {
642:
643: Tree delete = _delete;
644: _delete = null;
645:
646: delete.traverse(new Visitor4() {
647: public void visit(Object a_object) {
648: DeleteInfo info = (DeleteInfo) a_object;
649: // if the object has been deleted
650: if (isDeleted(info._key)) {
651: return;
652: }
653:
654: // We need to hold a hard reference here, otherwise we can get
655: // intermediate garbage collection kicking in.
656: Object obj = null;
657:
658: if (info._reference != null) {
659: obj = info._reference.getObject();
660: }
661: if (obj == null || info._reference.getID() < 0) {
662:
663: // This means the object was gc'd.
664:
665: // Let's try to read it again, but this may fail in
666: // CS mode if another transaction has deleted it.
667:
668: HardObjectReference hardRef = container()
669: .getHardObjectReferenceById(
670: LocalTransaction.this ,
671: info._key);
672: if (hardRef == HardObjectReference.INVALID) {
673: return;
674: }
675: info._reference = hardRef._reference;
676: info._reference.flagForDelete(container()
677: .topLevelCallId());
678: obj = info._reference.getObject();
679: }
680: container().delete3(LocalTransaction.this ,
681: info._reference, info._cascade, false);
682: }
683: });
684: }
685: _writtenUpdateDeletedMembers = null;
686: }
687:
688: public void writeUpdateDeleteMembers(int id, ClassMetadata clazz,
689: int typeInfo, int cascade) {
690:
691: checkSynchronization();
692:
693: if (DTrace.enabled) {
694: DTrace.WRITE_UPDATE_DELETE_MEMBERS.log(id);
695: }
696:
697: TreeInt newNode = new TreeInt(id);
698: _writtenUpdateDeletedMembers = Tree.add(
699: _writtenUpdateDeletedMembers, newNode);
700: if (!newNode.wasAddedToTree()) {
701: return;
702: }
703:
704: if (clazz.canUpdateFast()) {
705: slotFreeOnCommit(id, getCurrentSlotOfID(id));
706: return;
707: }
708:
709: StatefulBuffer objectBytes = container().readWriterByID(this ,
710: id);
711: if (objectBytes == null) {
712: if (clazz.hasClassIndex()) {
713: dontRemoveFromClassIndex(clazz.getID(), id);
714: }
715: return;
716: }
717:
718: ObjectHeader oh = new ObjectHeader(container(), clazz,
719: objectBytes);
720:
721: DeleteInfo info = (DeleteInfo) TreeInt.find(_delete, id);
722: if (info != null) {
723: if (info._cascade > cascade) {
724: cascade = info._cascade;
725: }
726: }
727:
728: objectBytes.setCascadeDeletes(cascade);
729: clazz.deleteMembers(oh._marshallerFamily, oh._headerAttributes,
730: objectBytes, typeInfo, true);
731: slotFreeOnCommit(id, new Slot(objectBytes.getAddress(),
732: objectBytes.length()));
733: }
734:
735: private Callbacks callbacks() {
736: return container().callbacks();
737: }
738:
739: private CallbackObjectInfoCollections collectCallbackObjectInfos(
740: ServerMessageDispatcher serverMessageDispatcher) {
741: if (null == _slotChanges) {
742: return CallbackObjectInfoCollections.EMTPY;
743: }
744: final Collection4 added = new Collection4();
745: final Collection4 deleted = new Collection4();
746: final Collection4 updated = new Collection4();
747: _slotChanges.traverseLocked(new Visitor4() {
748: public void visit(Object obj) {
749: SlotChange slotChange = ((SlotChange) obj);
750: LazyObjectReference lazyRef = new LazyObjectReference(
751: LocalTransaction.this , slotChange._key);
752: if (slotChange.isDeleted()) {
753: deleted.add(lazyRef);
754: } else if (slotChange.isNew()) {
755: added.add(lazyRef);
756: } else {
757: updated.add(lazyRef);
758: }
759: }
760: });
761: return new CallbackObjectInfoCollections(
762: serverMessageDispatcher, new ObjectInfoCollectionImpl(
763: added), new ObjectInfoCollectionImpl(updated),
764: new ObjectInfoCollectionImpl(deleted));
765: }
766:
767: private void setAddress(int a_address) {
768: i_address = a_address;
769: }
770:
771: public static Transaction readInterruptedTransaction(
772: LocalObjectContainer file, Buffer reader) {
773: int transactionID1 = reader.readInt();
774: int transactionID2 = reader.readInt();
775: if ((transactionID1 > 0) && (transactionID1 == transactionID2)) {
776: LocalTransaction transaction = (LocalTransaction) file
777: .newTransaction(null, null);
778: transaction.setAddress(transactionID1);
779: return transaction;
780: }
781: return null;
782: }
783:
784: private FreespaceManager freespaceManager() {
785: return _file.freespaceManager();
786: }
787:
788: private void freespaceBeginCommit() {
789: if (freespaceManager() == null) {
790: return;
791: }
792: freespaceManager().beginCommit();
793: }
794:
795: private void freespaceEndCommit() {
796: if (freespaceManager() == null) {
797: return;
798: }
799: freespaceManager().endCommit();
800: }
801:
802: private void commitFreespace() {
803: if (freespaceManager() == null) {
804: return;
805: }
806: freespaceManager().commit();
807: }
808:
809: }
|