001: /*
002: Copyright (C) 2007 Mobixess Inc. http://www.java-objects-database.com
003:
004: This file is part of the JODB (Java Objects Database) open source project.
005:
006: JODB is free software; you can redistribute it and/or modify it under
007: the terms of version 2 of the GNU General Public License as published
008: by the Free Software Foundation.
009:
010: JODB is distributed in the hope that it will be useful, but WITHOUT ANY
011: WARRANTY; without even the implied warranty of MERCHANTABILITY or
012: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
013: for more details.
014:
015: You should have received a copy of the GNU General Public License along
016: with this program; if not, write to the Free Software Foundation, Inc.,
017: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
018: */
019: package com.mobixess.jodb.core.transaction;
020:
021: import java.io.IOException;
022: import java.lang.reflect.Array;
023: import java.lang.reflect.Field;
024: import java.nio.ByteBuffer;
025: import java.util.IdentityHashMap;
026: import java.util.Iterator;
027: import java.util.LinkedHashMap;
028: import java.util.Map;
029: import java.util.Vector;
030: import java.util.WeakHashMap;
031: import java.util.concurrent.locks.ReentrantReadWriteLock;
032: import java.util.logging.Logger;
033:
034: import com.mobixess.jodb.core.IllegalClassTypeException;
035: import com.mobixess.jodb.core.JODBConfig;
036: import com.mobixess.jodb.core.JodbIOException;
037: import com.mobixess.jodb.core.agent.JODBAgent;
038: import com.mobixess.jodb.core.index.IndexingRecord;
039: import com.mobixess.jodb.core.index.JODBIndexingAgent;
040: import com.mobixess.jodb.core.io.IRandomAccessDataBuffer;
041: import com.mobixess.jodb.core.io.JODBOperationContext;
042: import com.mobixess.jodb.core.io.IRandomAccessBufferFactory.BUFFER_TYPE;
043: import com.mobixess.jodb.core.transaction.JODBSession.ClassDescriptor;
044: import com.mobixess.jodb.core.transaction.JODBSession.FieldAndIDRecord;
045: import com.mobixess.jodb.util.Utils;
046:
047: /**
048: * @author Mobixess
049: *
050: */
051: public class TransactionContainer implements ITranslatedDataSorce {
052: private Map<Object, TransactionHandle> _transactionObjects;
053: private Map<Object, TransactionHandle> _agentsTransactionObjects;
054: private WeakHashMap<ITransactionListener, Object> _transactionListeners = new WeakHashMap<ITransactionListener, Object>();
055:
056: private JODBSession _session;
057:
058: private IRandomAccessDataBuffer _newDataBuffer;
059: private IRandomAccessDataBuffer _replacementsBuffer;
060: private IRandomAccessDataBuffer _rollbackBuffer;
061:
062: private int _agentsMode;
063:
064: private ReentrantReadWriteLock _lock = new ReentrantReadWriteLock();
065:
066: protected Logger _logger = Utils.getLogger(getClass().getName());
067:
068: public TransactionContainer(JODBSession session) {
069: super ();
070: if (JODBConfig.DEBUG) {
071: _transactionObjects = new LinkedHashMap<Object, TransactionHandle>();
072: _agentsTransactionObjects = new LinkedHashMap<Object, TransactionHandle>();
073: } else {
074: _transactionObjects = new IdentityHashMap<Object, TransactionHandle>();
075: _agentsTransactionObjects = new IdentityHashMap<Object, TransactionHandle>();
076: }
077: _lock = session.getBase().getTransactionLock();
078: _session = session;
079: }
080:
081: /*package*/Map<Object, TransactionHandle> getTransactionObjects() {
082: return isAgentsMode() ? _agentsTransactionObjects
083: : _transactionObjects;
084: }
085:
086: public void lockTransaction() throws IOException {
087: _lock.writeLock().lock();
088: _newDataBuffer = JODBConfig.getRandomAccessBufferFactory()
089: .createBuffer("", BUFFER_TYPE.NEW_DATA, true);
090:
091: _rollbackBuffer = JODBConfig.getRandomAccessBufferFactory()
092: .createBuffer("", BUFFER_TYPE.ROLLBACK, true);
093:
094: _replacementsBuffer = JODBConfig.getRandomAccessBufferFactory()
095: .createBuffer("", BUFFER_TYPE.REPLACEMENTS, true);
096: }
097:
098: public TransactionHandle getHandleForObject(Object key) {
099: // TODO add lock?
100: return isAgentsMode() ? _agentsTransactionObjects.get(key)
101: : _transactionObjects.get(key);
102: }
103:
104: public void resetTranslatedObjects(JODBSession session,
105: long transactionShift) {
106: if (isAgentsMode()) {
107: throw new IllegalStateException();
108: }
109: resetTranslatedObjects0(session, _transactionObjects,
110: transactionShift);
111: resetTranslatedObjects0(session, _agentsTransactionObjects,
112: transactionShift);
113: }
114:
115: public void addTransactionListener(ITransactionListener listener) {
116: synchronized (_transactionListeners) {
117: _transactionListeners.put(listener, null);
118: }
119: }
120:
121: public void removeTransactionListeners(ITransactionListener listener) {
122: synchronized (_transactionListeners) {
123: _transactionListeners.remove(listener);
124: }
125: }
126:
127: void fireOnObjectSet(Object object, JODBSession session) {
128: synchronized (_transactionListeners) {
129: Iterator<ITransactionListener> iterator = _transactionListeners
130: .keySet().iterator();
131: while (iterator.hasNext()) {
132: ITransactionListener element = iterator.next();
133: element.onSet(object, session);
134: }
135: }
136: }
137:
138: void fireOnCommitStarted(Object object, JODBSession session) {
139: synchronized (_transactionListeners) {
140: Iterator<ITransactionListener> iterator = _transactionListeners
141: .keySet().iterator();
142: while (iterator.hasNext()) {
143: ITransactionListener element = iterator.next();
144: element.onCommitStarted(object, session);
145: }
146: }
147: }
148:
149: void fireOnCommitFinished(Object object, JODBSession session) {
150: synchronized (_transactionListeners) {
151: Iterator<ITransactionListener> iterator = _transactionListeners
152: .keySet().iterator();
153: while (iterator.hasNext()) {
154: ITransactionListener element = iterator.next();
155: element.onCommitFinished(object, session);
156: }
157: }
158: }
159:
160: private void resetTranslatedObjects0(JODBSession session,
161: Map<Object, TransactionHandle> transactionObjects,
162: long transactionShift) {
163: if (transactionObjects.isEmpty()) {
164: return;
165: }
166: Iterator<Object> iter = transactionObjects.keySet().iterator();
167: while (iter.hasNext()) {
168: Object element = iter.next();
169: TransactionHandle tHandle = transactionObjects.get(element);
170: if (tHandle.isNewObject()) {
171: PersistentObjectHandle persistentObjectHandle = session
172: .createHandleForObject(element, tHandle
173: .getTranslatedObjectDataMask(), tHandle
174: .getTransactionOffset()
175: + transactionShift);
176: _session.putObject(element, persistentObjectHandle);
177: }
178: iter.remove();
179: }
180: //_transactionObjects.clear();
181: }
182:
183: public void processTranslatedObjectsIndex(
184: JODBOperationContext context, long transactionShift)
185: throws IOException {
186: enableAgentMode();
187: try {
188: Iterator<Object> iter = _transactionObjects.keySet()
189: .iterator();
190: while (iter.hasNext()) {
191: Object element = iter.next();
192: TransactionHandle tHandle = _transactionObjects
193: .get(element);
194: long offsetId;
195: if (tHandle.isNewObject()) {
196: offsetId = tHandle.getTransactionOffset()
197: + transactionShift;
198: } else {
199: offsetId = tHandle.getHandle()
200: .getObjectEntryOffset();
201: }
202: Vector<IndexingRecord> indexingRecords = tHandle
203: .getIndexes();
204: if (indexingRecords != null) {
205: for (int i = 0; i < indexingRecords.size(); i++) {
206: IndexingRecord record = indexingRecords
207: .elementAt(i);
208: ByteBuffer currentValue = record
209: .getPersistedDataBuffer();
210: JODBIndexingAgent indexingAgent = record
211: .getIndexingAgent();
212: if (currentValue.limit() != 0
213: && !indexingAgent.removeIndex(offsetId,
214: currentValue, null)) {
215: throw new JodbIOException(
216: "Illegal index state: can't remove index");
217: }
218: ByteBuffer pendingValue = record
219: .getPendingDataBuffer();
220: if (pendingValue.limit() == 0) {
221: throw new JodbIOException(
222: "indexing value unavailable for "
223: + offsetId);
224: }
225: indexingAgent.insertIndex(offsetId,
226: pendingValue, context);
227: if (!_agentsTransactionObjects
228: .containsKey(indexingAgent)) {
229: try {
230: set(indexingAgent, Integer.MAX_VALUE);
231: } catch (Exception e) {
232: throw new JodbIOException(e);
233: }
234: }
235: }
236: }
237: }
238: } finally {
239: disableAgentMode();
240: }
241: }
242:
243: public void reset() {
244: try {
245: _newDataBuffer.delete();
246: _replacementsBuffer.delete();
247: _rollbackBuffer.delete();
248: _newDataBuffer = null;
249: _replacementsBuffer = null;
250: _rollbackBuffer = null;
251: _transactionObjects.clear();
252: _agentsMode = 0;
253: } catch (IOException e) {
254: throw new RuntimeException(e);
255: } finally {
256: _lock.writeLock().unlock();
257: }
258: }
259:
260: public void set(Object obj, int depth)
261: throws IllegalClassTypeException, IllegalStateException {
262: _lock.readLock().lock();
263: try {
264: set0(obj, depth, JODBConfig.isGenerateCommitTimeStamp(),
265: JODBConfig.isGenerateModificationTimeStamp(),
266: isAgentsMode() ? _agentsTransactionObjects
267: : _transactionObjects);
268: } finally {
269: _lock.readLock().unlock();
270: }
271: }
272:
273: public boolean isEmpty() {
274: return _transactionObjects.size() == 0
275: && _agentsTransactionObjects.size() == 0;
276: }
277:
278: private synchronized void set0(Object obj, int depth,
279: boolean creationTS, boolean modificationTS,
280: Map<Object, TransactionHandle> transactionObjects)
281: throws IllegalClassTypeException {
282: if (depth <= 0) {
283: return;
284: }
285: if (!isAgentsMode() && obj instanceof JODBAgent) {
286: throw new IllegalArgumentException(
287: "Cannot accept agent object");
288: }
289: TransactionHandle transObjectHandle = transactionObjects
290: .get(obj);
291: if (transObjectHandle != null) {
292: transObjectHandle.enable_SET_TransactionState();
293: setChildren(obj, depth - 1, creationTS, modificationTS,
294: transactionObjects);
295: return;
296: }
297: fireOnObjectSet(obj, _session);
298: PersistentObjectHandle handle = _session
299: .getHandleForActiveObject(obj);
300: if (handle != null) {
301: transObjectHandle = new TransactionHandle(handle);
302: } else {
303: transObjectHandle = new TransactionHandle(creationTS,
304: modificationTS);
305: }
306:
307: transObjectHandle.setAgentObjectMode(isAgentsMode());
308: // if(JODBConfig.DEBUG){
309: // _logger.warning("setting transaction object "+obj);
310: // }
311: transactionObjects.put(obj, transObjectHandle);
312: transObjectHandle.enable_SET_TransactionState();
313: setChildren(obj, depth - 1, creationTS, modificationTS,
314: transactionObjects);
315: }
316:
317: private void setChildren(Object obj, int depth, boolean creationTS,
318: boolean modificationTS,
319: Map<Object, TransactionHandle> transactionObjects)
320: throws IllegalClassTypeException {
321: if (depth <= 0) {
322: return;
323: }
324: ClassDescriptor descr = _session.getDescriptorForClass(obj
325: .getClass());
326: if (descr.isArray()) {
327: if (!descr.isPrimitiveArray()) {
328: int length = Array.getLength(obj);
329: for (int i = 0; i < length; i++) {
330: Object value = Array.get(obj, i);
331: if (value != null) {
332: set0(value, depth, creationTS, modificationTS,
333: transactionObjects);
334: }
335: }
336: }
337: } else {
338: FieldAndIDRecord[] fields = descr.getFields();
339: for (int i = 0; i < fields.length; i++) {
340: Field field = fields[i]._field;
341: if (field.getType().isPrimitive()) {
342: continue;
343: }
344: try {
345: Object value = field.get(obj);
346: if (value != null) {
347: set0(value, depth, creationTS, modificationTS,
348: transactionObjects);
349: }
350: } catch (Exception e) {
351: e.printStackTrace();
352: throw new RuntimeException(e);
353: }
354: }
355: }
356: }
357:
358: public void delete(Object obj, int depth)
359: throws IllegalClassTypeException {
360: _lock.readLock().lock();
361: try {
362: delete0(obj, depth);
363: } finally {
364: _lock.readLock().unlock();
365: }
366: }
367:
368: public synchronized void delete0(Object obj, int depth)
369: throws IllegalClassTypeException {
370: if (depth <= 0) {
371: return;
372: }
373: TransactionHandle transObjectHandle = _transactionObjects
374: .get(obj);
375: if (transObjectHandle != null) {
376: if (transObjectHandle.isNewObject()) {
377: _transactionObjects.remove(obj);
378: } else {
379: transObjectHandle.enable_DELETE_TransactionState();
380: }
381: deleteChildren(obj, depth - 1);
382: return;
383: }
384: PersistentObjectHandle handle = _session
385: .getHandleForActiveObject(obj);
386: if (handle != null) {
387: transObjectHandle = new TransactionHandle(handle);
388: transObjectHandle.enable_DELETE_TransactionState();
389: _transactionObjects.put(obj, transObjectHandle);
390: }
391: deleteChildren(obj, depth - 1);
392: }
393:
394: public synchronized void deleteChildren(Object obj, int depth)
395: throws IllegalClassTypeException {
396: if (depth <= 0) {
397: return;
398: }
399: ClassDescriptor descr = _session.getDescriptorForClass(obj
400: .getClass());
401: FieldAndIDRecord[] fields = descr.getFields();
402: for (int i = 0; i < fields.length; i++) {
403: Field field = fields[i]._field;
404: if (field.getType().isPrimitive()) {
405: continue;
406: }
407: try {
408: Object value = field.get(obj);
409: if (value != null) {
410: delete0(value, depth);
411: }
412: } catch (Exception e) {
413: e.printStackTrace();
414: throw new RuntimeException(e);
415: }
416: }
417: }
418:
419: public void resetTransactionBufferToStart() throws IOException {
420: _rollbackBuffer.resetToStart();
421: _newDataBuffer.resetToStart();
422: _replacementsBuffer.resetToStart();
423: }
424:
425: public void resetTransactionBufferToEnd() throws IOException {
426: _rollbackBuffer.resetToEnd();
427: _newDataBuffer.resetToEnd();
428: _replacementsBuffer.resetToEnd();
429: }
430:
431: public IRandomAccessDataBuffer getRollbackDataFile() {
432: return _rollbackBuffer;
433: }
434:
435: public IRandomAccessDataBuffer getTransactionNewDataFile() {
436: return _newDataBuffer;
437: }
438:
439: public IRandomAccessDataBuffer getTransactionReplacementsDataFile() {
440: return _replacementsBuffer;
441: }
442:
443: public void enableAgentMode() {
444: _agentsMode++;
445: }
446:
447: public void disableAgentMode() {
448: _agentsMode--;
449: if (_agentsMode < 0) {
450: throw new IllegalStateException();
451: }
452: }
453:
454: public boolean isAgentsMode() {
455: return _agentsMode != 0;
456: }
457: }
|