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.DataOutput;
022: import java.io.IOException;
023: import java.lang.ref.WeakReference;
024: import java.lang.reflect.Field;
025: import java.nio.ByteBuffer;
026: import java.util.Vector;
027:
028: import com.mobixess.jodb.core.JodbIOException;
029: import com.mobixess.jodb.core.index.IndexingRecord;
030: import com.mobixess.jodb.core.io.IOTicket;
031: import com.mobixess.jodb.core.io.IRandomAccessDataBuffer;
032: import com.mobixess.jodb.core.io.IOBase;
033: import com.mobixess.jodb.core.io.JODBIOBase;
034: import com.mobixess.jodb.core.io.ObjectDataContainer;
035: import com.mobixess.jodb.core.io.ObjectDataContainer.FieldsIterator;
036: import com.mobixess.jodb.core.plugin.IClassProcessor;
037: import com.mobixess.jodb.core.plugin.JODBPluginRegistry;
038: import com.mobixess.jodb.core.transaction.JODBSession.ClassDescriptor;
039: import com.mobixess.jodb.util.ArrayUtils;
040: import com.mobixess.jodb.util.Utils;
041:
042: /**
043: * @author Mobixess
044: *
045: */
046: public class TransactionUtils {
047:
048: private final static int MAX_DATA_CONTAINERS_CACHE = 100;
049:
050: // private static class LaunchObjectsLevelsCache{
051: // private Vector<WeakReference<Vector<FieldRecord>>> _cache = new Vector<WeakReference<Vector<FieldRecord>>>();
052: //
053: // public Vector<FieldRecord> getFieldRecordCache(int level){
054: // if(level >= _cache.size()){
055: // _cache.setSize(level+1);
056: // }
057: // WeakReference<Vector<FieldRecord>> weakReference = _cache.elementAt(level);
058: // Vector<FieldRecord> result = null;
059: // if(weakReference == null || (result = weakReference.get()) == null){
060: // result = new Vector<FieldRecord>();
061: // weakReference = new WeakReference<Vector<FieldRecord>>(result);
062: // _cache.setElementAt(weakReference, level);
063: // }
064: // result.setSize(0);
065: // return result;
066: // }
067: // }
068:
069: @SuppressWarnings("unchecked")
070: public static class DataContainersCache {
071:
072: private WeakRefObjectDataContainerCache _objectDataContainerCache = new WeakRefObjectDataContainerCache();
073: private WeakRefIndexContainerCache _weakRefIndexContainerCache = new WeakRefIndexContainerCache();
074: private WeakRefVectorCache _weakRefVectorCache = new WeakRefVectorCache();
075:
076: private DataContainersCache() {
077: }
078:
079: public ObjectDataContainer pullObjectDataContainer() {
080: return _objectDataContainerCache.pull();
081: }
082:
083: public void pushObjectDataContainer(
084: ObjectDataContainer container) {
085: _objectDataContainerCache.push(container);
086: }
087:
088: public IndexingRecord pullIndexRecord() {
089: return _weakRefIndexContainerCache.pull();
090: }
091:
092: public void pushIndexRecord(IndexingRecord byteBuffer) {
093: _weakRefIndexContainerCache.push(byteBuffer);
094: }
095:
096: public void pushVector(Vector container) {
097: _weakRefVectorCache.push(container);
098: }
099:
100: public Vector pullVector() {
101: return _weakRefVectorCache.pull();
102: }
103:
104: }
105:
106: private static class WeakRefObjectDataContainerCache extends
107: WeakRefDataContainerCache<ObjectDataContainer> {
108:
109: public WeakRefObjectDataContainerCache() {
110: super (MAX_DATA_CONTAINERS_CACHE);
111: }
112:
113: @Override
114: public ObjectDataContainer pull() {
115: ObjectDataContainer result = super .pull();
116: if (result == null) {
117: result = new ObjectDataContainer();
118: }
119: return result;
120: }
121:
122: @Override
123: public void push(ObjectDataContainer container) {
124: container.reset();
125: super .push(container);
126: }
127: }
128:
129: private static class WeakRefVectorCache extends
130: WeakRefDataContainerCache<Vector> {
131:
132: public WeakRefVectorCache() {
133: super (10);
134: }
135:
136: @Override
137: public Vector pull() {
138: Vector result = super .pull();
139: if (result == null) {
140: result = new Vector(2);
141: }
142: return result;
143: }
144:
145: @Override
146: public void push(Vector container) {
147: container.clear();
148: super .push(container);
149: }
150:
151: }
152:
153: private static class WeakRefIndexContainerCache extends
154: WeakRefDataContainerCache<IndexingRecord> {
155: public WeakRefIndexContainerCache() {
156: super (4);
157: }
158:
159: @Override
160: public IndexingRecord pull() {
161: IndexingRecord indexingRecord = super .pull();
162: if (indexingRecord == null) {
163: indexingRecord = new IndexingRecord();
164: }
165: indexingRecord.reset();
166: return indexingRecord;
167: }
168: }
169:
170: private static class WeakRefDataContainerCache<ContainerType> {
171: private WeakReference<ContainerType>[] _pull;
172: private int _index;
173:
174: @SuppressWarnings("unchecked")
175: public WeakRefDataContainerCache(int size) {
176: _pull = new WeakReference[size];
177: }
178:
179: public ContainerType pull() {
180: WeakReference<ContainerType> reference;
181: for (; _index >= 0; _index--) {
182: reference = _pull[_index];
183: if (reference != null) {
184: ContainerType result = reference.get();
185: _pull[_index] = null;
186: if (result != null) {
187: return result;
188: }
189: }
190: }
191: return null;
192: }
193:
194: public void push(ContainerType container) {
195: if (_index == _pull.length - 1) {
196: return;
197: }
198: _pull[++_index] = new WeakReference<ContainerType>(
199: container);
200: }
201: }
202:
203: // private static ThreadLocal<WeakReference<LaunchObjectsLevelsCache>> _launchObjectCache = new ThreadLocal<WeakReference<LaunchObjectsLevelsCache>>() {
204: // @Override
205: // protected WeakReference<LaunchObjectsLevelsCache> initialValue()
206: // {
207: // return new WeakReference<LaunchObjectsLevelsCache>(new LaunchObjectsLevelsCache());
208: // }
209: // };
210:
211: private static ThreadLocal<WeakReference<DataContainersCache>> _objectDataContainerCacheWeakRef = new ThreadLocal<WeakReference<DataContainersCache>>() {
212: @Override
213: protected WeakReference<DataContainersCache> initialValue() {
214: return new WeakReference<DataContainersCache>(
215: new DataContainersCache());
216: }
217: };
218:
219: public static boolean compareToPersistentValues(
220: JODBSession session, Field[] fields, int[] fieldsIDs,
221: Object currentObject, IOTicket ioTicket,
222: ObjectDataContainer headerData) throws IOException {
223: boolean[] processedFields = new boolean[fieldsIDs.length];
224: IRandomAccessDataBuffer in = ioTicket.getRandomAccessBuffer();
225: short classNameIDs = in.readShort();
226: for (int i = 0; i < classNameIDs; i++) {
227: in.readShort();
228: }
229:
230: //null fields no longer persisted
231: // if(headerData.hasNullFields()){
232: // int nullFieldsTotal = in.readShort()&0xffff;
233: // for (int i = 0; i < nullFieldsTotal; i++) {
234: // int id = in.readShort()&0xffff;
235: // int index = ArrayUtils.indexOf(fieldsIDs, id);
236: // if(index == -1){
237: // continue;
238: // }
239: // Field field = fields[index];
240: // try {
241: // if(field.get(currentObject)!=null){//TODO may need additional check if object is in transaction, rare case though
242: // return false;
243: // }
244: // } catch (Exception e) {
245: // e.printStackTrace();
246: // throw new IOException(e.getMessage());
247: // }
248: // processedFields[index] = true;
249: // }
250: // }
251:
252: if (headerData.hasDirectlyAddressedFields()) {
253: boolean result = verifyLinks(session, false, fields,
254: fieldsIDs, processedFields, currentObject, ioTicket);
255: if (!result) {
256: return false;
257: }
258: }
259:
260: if (headerData.hasRelativelyAddressedFields()) {
261: boolean result = verifyLinks(session, true, fields,
262: fieldsIDs, processedFields, currentObject, ioTicket);
263: if (!result) {
264: return false;
265: }
266: }
267:
268: //long endOffset = headerData.getOffset() + headerData.getTotalLength();
269:
270: if (headerData.hasPrimitiveFields()) {
271: int total = in.readShort() & 0xFFFF;
272: for (int i = 0; i < total; i++) {
273: int id = in.readShort() & 0xffff;
274: int index = ArrayUtils.indexOf(fieldsIDs, id);
275: Field field = fields[index];
276: if (index == -1) {
277: Utils.skipPrimitive(field, ioTicket
278: .getRandomAccessBuffer());
279: continue;
280: }
281: if (!Utils.readPrimitiveAndCompare(currentObject,
282: field, in)) {
283: return false;
284: }
285: processedFields[index] = true;
286: }
287: }
288: for (int i = 0; i < fieldsIDs.length; i++) {//cheking if remaining not found fields have default values
289: if (fieldsIDs[i] == -1 || processedFields[i]) {
290: continue;
291: }
292: Field field = fields[i];
293: if (field.getType().isPrimitive()) {
294: if (!Utils.primitiveEquals(field, currentObject, 0)) {
295: return false;
296: }
297: } else {
298: Object value;
299: try {
300: value = field.get(currentObject);
301: } catch (Exception e) {
302: e.printStackTrace();
303: throw new JodbIOException(e);
304: }
305: if (value != null) {
306: return false;
307: }
308: }
309: }
310:
311: return true;
312: }
313:
314: public static void writeEmptyObjectEntry(DataOutput dataOutput,
315: long len) throws IOException {
316: if (len <= 0xff) {
317: dataOutput.writeShort(JODBIOBase.ENTRY_EMPTY_ID
318: | JODBIOBase.LEN_MODIFIER_BYTE);
319: dataOutput.write((int) len);
320: } else if (len <= 0xffff) {
321: dataOutput.writeShort(JODBIOBase.ENTRY_EMPTY_ID);
322: dataOutput.writeShort((int) len);
323: } else {
324: dataOutput.writeShort(JODBIOBase.ENTRY_EMPTY_ID
325: | JODBIOBase.LEN_MODIFIER_LONG);
326: dataOutput.writeLong(len);
327: }
328: }
329:
330: public static void writeRedirectionEntry(DataOutput dataOutput,
331: long len, long offset, boolean skipLength)
332: throws IOException {
333: if (len <= 0xff) {
334: dataOutput.write(JODBIOBase.ENTRY_REDIRECTOR_ID
335: | JODBIOBase.LEN_MODIFIER_BYTE);
336: dataOutput.write((int) len);
337: } else if (len <= 0xffff) {
338: dataOutput.writeShort(JODBIOBase.ENTRY_REDIRECTOR_ID);
339: dataOutput.writeShort((int) len);
340: } else {
341: dataOutput.writeShort(JODBIOBase.ENTRY_REDIRECTOR_ID
342: | JODBIOBase.LEN_MODIFIER_LONG);
343: dataOutput.writeLong(len);
344: }
345: dataOutput.writeLong(offset);
346: }
347:
348: private static boolean verifyLinks(JODBSession session,
349: boolean isRelativeAddr, Field[] fields, int[] fieldsIDs,
350: boolean[] processedFields, Object currentObject,
351: IOTicket ioTicket) throws IOException {
352: IRandomAccessDataBuffer in = ioTicket.getRandomAccessBuffer();
353: int directlyAddressedFieldsTotal = in.readShort() & 0xffff;
354: for (int i = 0; i < directlyAddressedFieldsTotal; i++) {
355: int id = in.readShort() & 0xffff;
356: long offset;
357: if (isRelativeAddr) {
358: offset = in.readInt() + in.getCursorOffset();
359: } else {
360: offset = in.readLong();
361: }
362: int index = ArrayUtils.indexOf(fieldsIDs, id);
363: if (index == -1) {
364: continue;
365: }
366: Field field = fields[index];
367: try {
368: Object value = field.get(currentObject);
369: if (value == null) {//this field can't be null
370: return false;
371: }
372: PersistentObjectHandle handle = session
373: .getHandleForActiveObject(value);
374: if (handle == null) {//this field must contain known object
375: return false;
376: }
377: if (handle.getObjectEntryOffset() == offset) {//same offset - object verified
378: processedFields[index] = true;
379: continue;
380: }
381: } catch (Exception e) {
382: e.printStackTrace();
383: throw new JodbIOException(e);
384: }
385: }
386: return true;
387: }
388:
389: public static Object launchObject(JODBSession session, long offset,
390: Object activationInstance, int remainingDepth)
391: throws IOException {
392: if (remainingDepth < 0 || offset == 0) {
393: return null;
394: }
395: IOBase base = session.getBase();
396: boolean delayedActivation = activationInstance != null;
397: Object instance;
398: if (!delayedActivation) {//this is user's requested activation call, instance already exist
399: instance = session.getObjectFromCache(offset);
400: if (instance != null) {
401: return instance;
402: }
403: } else {
404: instance = activationInstance;
405: }
406: IOTicket ioTicket = base.getIOTicket(true, false);
407: try {
408: DataContainersCache dataContainersCache = getObjectDataContainerCache();
409: ObjectDataContainer objectDataContainer = dataContainersCache
410: .pullObjectDataContainer();
411: FieldsIterator fieldsIterator = objectDataContainer
412: .readObject(ioTicket.getRandomAccessBuffer(), base,
413: session, offset, true);
414: ClassDescriptor classDescriptor = objectDataContainer
415: .getClassDescriptorForPersistedObject();
416: if (classDescriptor == null || fieldsIterator == null) {
417: // basically same as >>objectDataContainer.isDeleted()<< or no class available for persistent copy
418: return null;
419: }
420: IClassProcessor processor;
421: if (!delayedActivation) {
422: int classTypeId = objectDataContainer
423: .getOriginalClassType();
424: String className = base.getClassTypeForID(classTypeId);
425: Class clazz;
426: try {
427: clazz = session.resolveClassForName(className);
428: } catch (ClassNotFoundException e) {
429: return null;
430: }
431: processor = JODBPluginRegistry.getInstance()
432: .getClassProcessor(clazz);
433: instance = processor.composeInstance(clazz,
434: objectDataContainer, session);
435: } else {
436: processor = JODBPluginRegistry.getInstance()
437: .getClassProcessor(instance.getClass());
438: }
439: PersistentObjectHandle handle = session
440: .createHandleForObject(instance,
441: objectDataContainer.getPrimaryDataMask(),
442: offset);
443: synchronized (handle) {//TODO "get object" must obey this lock or incompletely initialized object could become accessible
444: if (!delayedActivation) {
445: synchronized (session.getActivationSynchObject()) {//put object into cache to prevent potential recursion
446: //check if some other thread already instantiated the object
447: Object candidate = session
448: .getObjectFromCache(offset);
449: if (candidate != null) {
450: dataContainersCache
451: .pushObjectDataContainer(objectDataContainer);//return container to cache
452: return candidate;
453: }
454: session.putObject(instance, handle);
455: }
456: }
457:
458: processor.activate(instance, objectDataContainer,
459: session, remainingDepth, delayedActivation);
460: dataContainersCache
461: .pushObjectDataContainer(objectDataContainer);//return container to cache
462: objectDataContainer = null;
463: }
464: } finally {
465: ioTicket.close();
466: }
467: return instance;
468: }
469:
470: public void deactivate(ClassDescriptor classDescriptor, Object obj,
471: int depth) throws IOException {
472: deactivate0(classDescriptor, obj, depth, 0);
473: }
474:
475: private void deactivate0(ClassDescriptor classDescriptor,
476: Object obj, int depth, int level) throws IOException {
477: //Vector<FieldRecord> records = getFieldsContainerForLevel(level);
478: //TODO
479: }
480:
481: public static DataContainersCache getObjectDataContainerCache() {
482: WeakReference<DataContainersCache> reference = _objectDataContainerCacheWeakRef
483: .get();
484: DataContainersCache cache = reference.get();
485: if (cache == null) {
486: cache = new DataContainersCache();
487: _objectDataContainerCacheWeakRef
488: .set(new WeakReference<DataContainersCache>(cache));
489: }
490: return cache;
491: }
492:
493: // private static Vector<FieldRecord> getFieldsContainerForLevel(int level){
494: // WeakReference<LaunchObjectsLevelsCache> launchObjectsLevelsCache = _launchObjectCache.get();
495: // LaunchObjectsLevelsCache cache = launchObjectsLevelsCache.get();
496: // if(cache == null){
497: // cache = new LaunchObjectsLevelsCache();
498: // launchObjectsLevelsCache = new WeakReference<LaunchObjectsLevelsCache>(cache);
499: // _launchObjectCache.set(launchObjectsLevelsCache);
500: // }
501: // return cache.getFieldRecordCache(level);
502: // }
503:
504: }
|