/*
 NeoDatis ODB : Native Object Database (odb.info@neodatis.org)
 Copyright (C) 2007 NeoDatis Inc. http://www.neodatis.org

 "This file is part of the NeoDatis ODB open source object database".

 NeoDatis ODB is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 NeoDatis ODB is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.neodatis.odb.impl.core.transaction;

import java.util.Map;

import org.neodatis.odb.ODBRuntimeException;
import org.neodatis.odb.OID;
import org.neodatis.odb.OdbConfiguration;
import org.neodatis.odb.core.NeoDatisError;
import org.neodatis.odb.core.layers.layer2.meta.NonNativeObjectInfo;
import org.neodatis.odb.core.layers.layer2.meta.ObjectInfoHeader;
import org.neodatis.odb.core.transaction.ICache;
import org.neodatis.odb.core.transaction.ISession;
import org.neodatis.odb.impl.core.layers.layer3.engine.StorageEngineConstant;
import org.neodatis.odb.impl.core.layers.layer3.oid.IDStatus;
import org.neodatis.tool.wrappers.map.OdbHashMap;

/**
 * A cache of object.
 * 
 * 
 *   Cache objects by object, by position, by oids,...
 * 
* * @author olivier s * */ public class Cache implements ICache { protected static int nbObjects = 0; protected static int nbOids = 0; protected static int nbOih = 0; protected static int nbTransactionOids = 0; protected static int nbObjectPositionByIds = 0; protected static int nbCallsToGetObjectInfoHeaderFromOid = 0; protected static int nbCallsToGetObjectInfoHeaderFromObject = 0; protected static int nbCallsToGetObjectWithOid = 0; /** * object cache - used to know if object exist in the cache TODO use * hashcode instead? */ protected Map objects; /** Entry to get an object from its oid */ protected Map oids; /** To resolve cyclic reference, keep track of objects being inserted */ protected Map insertingObjects; /** To resolve cyclic reference, keep track of objects being read */ protected Map readingObjectInfo; /** *
	 *    To resolve the update of an id object position:
	 *    When an object is full updated(the current object is being deleted and a new one os being created), 
	 *    the id remain the same but its position change.
	 *    But the update is done in transaction, so it is not flushed until the commit happens
	 *    So after the update when i need the position to make the old object a pointer, i have no way to get 
	 *    the right position. To resolve this, i keep a cache of ids where i keep the non commited value
	 * 
* */ protected Map objectPositionsByIds; /** * To keep track of the oid that have been created or modified in the * current transaction */ protected Map unconnectedZoneOids; protected ISession session; protected String name; /** * Entry to get object info pointers (position,next object pos, previous * object pos and class info pos) from the id */ protected Map objectInfoPointersCacheFromOid; protected boolean useCache; public Cache(ISession session, String name) { init(session, name); } protected void init(ISession session, String name) { this.name = name; this.session = session; objects = new OdbHashMap(); oids = new OdbHashMap(); unconnectedZoneOids = new OdbHashMap(); objectInfoPointersCacheFromOid = new OdbHashMap(); insertingObjects = new OdbHashMap(); readingObjectInfo = new OdbHashMap(); objectPositionsByIds = new OdbHashMap(); useCache = OdbConfiguration.useCache(); } /* * * * Adds an object to the cache Add the object, the oid and the object infos * Keys to get objects are the oid. */ public void addObject(OID oid, Object object, ObjectInfoHeader objectInfoHeader) { if(!useCache){ return; } if (oid == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID); } if (checkHeaderPosition() && objectInfoHeader.getPosition() == -1) { throw new ODBRuntimeException(NeoDatisError.CACHE_NEGATIVE_POSITION.addParameter("Adding OIH with position = -1")); } // TODO : Should remove first inserted object and not clear all cache if (objects.size() > OdbConfiguration.getMaxNumberOfObjectInCache()) { // clear(); manageFullCache(); } oids.put(oid, object); try { objects.put(object, oid); } catch (NullPointerException e) { // FIXME URL in HashMap What should we do? // In some case, the object can throw exception when added to the // cache // because Map.put, end up calling the equals method that can throw // exception // This is the case of URL that has a transient attribute handler // that is used in the URL.equals method } objectInfoPointersCacheFromOid.put(oid, objectInfoHeader); // For monitoring purpose nbObjects = objects.size(); nbOids = oids.size(); nbOih = objectInfoPointersCacheFromOid.size(); } /** * Only adds the Object info - used for non committed objects * */ public void addObjectInfo(ObjectInfoHeader objectInfoHeader) { if(!useCache){ return; } if (objectInfoHeader.getOid() == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID); } if (objectInfoHeader.getClassInfoId() == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_OBJECT_INFO_HEADER_WITHOUT_CLASS_ID.addParameter(objectInfoHeader.getOid())); } // TODO : Should remove first inserted object and not clear all cache if (objectInfoPointersCacheFromOid.size() > OdbConfiguration.getMaxNumberOfObjectInCache()) { manageFullCache(); } objectInfoPointersCacheFromOid.put(objectInfoHeader.getOid(), objectInfoHeader); // For monitoring purpose nbObjects = objects.size(); nbOids = oids.size(); nbOih = objectInfoPointersCacheFromOid.size(); } protected void manageFullCache() { if (OdbConfiguration.automaticallyIncreaseCacheSize()) { OdbConfiguration.setMaxNumberOfObjectInCache((long) (OdbConfiguration.getMaxNumberOfObjectInCache() * 1.2)); } else { //throw new ODBRuntimeException(Error.CACHE_IS_FULL.addParameter(objectInfoPointersCacheFromOid.size()).addParameter(OdbConfiguration.getMaxNumberOfObjectInCache())); } } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#startInsertingObject(java.lang.Object, * long, org.neodatis.odb.core.meta.NonNativeObjectInfo) */ public void startInsertingObjectWithOid(Object object, OID oid, NonNativeObjectInfo nnoi) { // In this case oid can be -1,because object is beeing inserted and do // not have yet a defined oid. if (object == null) { return; } ObjectInsertingInfo oii = (ObjectInsertingInfo) insertingObjects.get(object); if (oii == null) { insertingObjects.put(object, new ObjectInsertingInfo(oid, 1)); } else { oii.level++; // No need to update the map, it is a reference. } } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#updateIdOfInsertingObject(java.lang.Object, * long) */ public void updateIdOfInsertingObject(Object object, OID oid) { if (oid == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID); } ObjectInsertingInfo oii = (ObjectInsertingInfo) insertingObjects.get(object); if (oii != null) { oii.oid = oid; } } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#endInsertingObject(java.lang.Object) */ public void endInsertingObject(Object object) { ObjectInsertingInfo oii = (ObjectInsertingInfo) insertingObjects.get(object); if (oii.level == 1) { insertingObjects.remove(object); oii = null; } else { oii.level--; } } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#removeObject(long, * java.lang.Object) */ public void removeObjectWithOid(OID oid) { if (oid == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID); } Object object = oids.get(oid); oids.remove(oid); try { objects.remove(object); } catch (NullPointerException e) { // FIXME URL in HashMap What should we do? } objectInfoPointersCacheFromOid.remove(oid); unconnectedZoneOids.remove(oid); // For monitoring purpose nbObjects = objects.size(); nbOids = oids.size(); nbOih = objectInfoPointersCacheFromOid.size(); } public void removeObject(Object object) { if (object == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OBJECT.addParameter(" while removing object from the cache")); } OID oid = (OID) objects.get(object); oids.remove(oid); try { objects.remove(object); } catch (NullPointerException e) { // FIXME URL in HashMap What should we do? } objectInfoPointersCacheFromOid.remove(oid); unconnectedZoneOids.remove(oid); // For monitoring purpose nbObjects = objects.size(); nbOids = oids.size(); nbOih = objectInfoPointersCacheFromOid.size(); } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#existObject(java.lang.Object) */ public boolean existObject(Object object) { return objects.containsKey(object); } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#getObjectWithPosition(long) */ public Object getObjectWithOid(OID oid) { if (oid == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID.addParameter(oid)); } Object o = oids.get(oid); nbCallsToGetObjectWithOid++; return o; } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#getObjectInfoHeaderFromObject(java.lang.Object, * boolean) */ public ObjectInfoHeader getObjectInfoHeaderFromObject(Object object, boolean throwExceptionIfNotFound) { OID oid = (OID) objects.get(object); ObjectInfoHeader oih = (ObjectInfoHeader) objectInfoPointersCacheFromOid.get(oid); if (oih == null && throwExceptionIfNotFound) { throw new ODBRuntimeException(NeoDatisError.OBJECT_DOES_NOT_EXIST_IN_CACHE.addParameter(object.toString())); } nbCallsToGetObjectInfoHeaderFromObject++; return oih; } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#getObjectInfoHeaderFromPosition(long, * boolean) */ public ObjectInfoHeader getObjectInfoHeaderFromOid(OID oid, boolean throwExceptionIfNotFound) { if (oid == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID); } ObjectInfoHeader oih = (ObjectInfoHeader) objectInfoPointersCacheFromOid.get(oid); if (oih == null && throwExceptionIfNotFound) { throw new ODBRuntimeException(NeoDatisError.OBJECT_WITH_OID_DOES_NOT_EXIST_IN_CACHE.addParameter(oid)); } nbCallsToGetObjectInfoHeaderFromOid++; return oih; } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#getObjectPosition(java.lang.Object) */ public OID getOid(Object object, boolean throwExceptionIfNotFound) { OID oid = (OID) objects.get(object); if (oid != null) { return oid; } if (throwExceptionIfNotFound) { throw new ODBRuntimeException(NeoDatisError.OBJECT_DOES_NOT_EXIST_IN_CACHE); } return StorageEngineConstant.NULL_OBJECT_ID; } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#saveObjectPosition(long, long) */ public void savePositionOfObjectWithOid(OID oid, long objectPosition) { if (oid == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID); } IdInfo idInfo = new IdInfo(oid, objectPosition, IDStatus.ACTIVE); objectPositionsByIds.put(oid, idInfo); // For monitoring purpose nbObjects = objects.size(); nbOids = oids.size(); nbOih = objectInfoPointersCacheFromOid.size(); } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#markIdAsDeleted(long) */ public void markIdAsDeleted(OID oid) { if (oid == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID); } IdInfo idInfo = objectPositionsByIds.get(oid); if (idInfo != null) { idInfo.status = IDStatus.DELETED; } else { idInfo = new IdInfo(oid, -1, IDStatus.DELETED); objectPositionsByIds.put(oid, idInfo); } } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#isDeleted(long) */ public boolean isDeleted(OID oid) { if (oid == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID); } IdInfo idInfo = (IdInfo) objectPositionsByIds.get(oid); if (idInfo != null) { return idInfo.status == IDStatus.DELETED; } return false; } /** * Returns the position or -1 if it is not is the cache or * StorageEngineConstant.NULL_OBJECT_ID_ID if it has been marked as deleted */ public long getObjectPositionByOid(OID oid) { if (oid == null) { return StorageEngineConstant.NULL_OBJECT_ID_ID; } IdInfo idInfo = (IdInfo) objectPositionsByIds.get(oid); if (idInfo != null) { if (!IDStatus.isActive(idInfo.status)) { return StorageEngineConstant.DELETED_OBJECT_POSITION; } return idInfo.position; } // object is not in the cache return StorageEngineConstant.OBJECT_IS_NOT_IN_CACHE; } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#clearOnCommit() */ public void clearOnCommit() { objectPositionsByIds.clear(); unconnectedZoneOids.clear(); objectInfoPointersCacheFromOid.clear(); } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#clear(boolean) */ public void clear(boolean setToNull) { if(objects!=null){ objects.clear(); oids.clear(); objectInfoPointersCacheFromOid.clear(); insertingObjects.clear(); objectPositionsByIds.clear(); readingObjectInfo.clear(); unconnectedZoneOids.clear(); } if (setToNull) { objects = null; oids = null; objectInfoPointersCacheFromOid = null; insertingObjects = null; objectPositionsByIds = null; readingObjectInfo = null; unconnectedZoneOids = null; } } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#clearInsertingObjects() */ public void clearInsertingObjects() { insertingObjects.clear(); } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#toString() */ public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("C="); buffer.append(objects.size()).append(" objects "); buffer.append(oids.size()).append(" oids "); buffer.append(objectInfoPointersCacheFromOid.size()).append(" pointers"); buffer.append(objectPositionsByIds.size()).append(" pos by oid"); // buffer.append(insertingObjects.size()).append(" inserting // objects\n"); // buffer.append(readingObjectInfo.size()).append(" reading objects"); return buffer.toString(); } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#toCompleteString() */ public String toCompleteString() { StringBuffer buffer = new StringBuffer(); buffer.append(objects.size()).append(" Objects=").append(objects).append("\n"); buffer.append(oids.size()).append(" Objects from pos").append(oids).append("\n"); buffer.append(objectInfoPointersCacheFromOid.size()).append(" Pointers=").append(objectInfoPointersCacheFromOid); return buffer.toString(); } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#getNumberOfObjects() */ public int getNumberOfObjects() { return objects.size(); } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#getNumberOfObjectHeader() */ public int getNumberOfObjectHeader() { return objectInfoPointersCacheFromOid.size(); } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#idOfInsertingObject(java.lang.Object) */ public OID idOfInsertingObject(Object object) { if (object == null) { return StorageEngineConstant.NULL_OBJECT_ID; } ObjectInsertingInfo oii = (ObjectInsertingInfo) insertingObjects.get(object); if (oii != null) { return oii.oid; } return StorageEngineConstant.NULL_OBJECT_ID; } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#insertingLevelOf(java.lang.Object) */ public int insertingLevelOf(Object object) { ObjectInsertingInfo oii = (ObjectInsertingInfo) insertingObjects.get(object); if (oii == null) { return 0; } return oii.level; } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#isReadingObjectInfo(long) */ public boolean isReadingObjectInfoWithOid(OID oid) { if (oid == null) { return false;// throw new // ODBRuntimeException(Error.CACHE_NULL_OID); } return readingObjectInfo.get(oid) != null; } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#getReadingObjectInfo(long) */ public NonNativeObjectInfo getReadingObjectInfoFromOid(OID oid) { if (oid == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID); } Object[] values = (Object[]) readingObjectInfo.get(oid); if(values==null){ return null; } return (NonNativeObjectInfo) values[1]; } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#startReadingObjectInfo(long, * org.neodatis.odb.core.meta.NonNativeObjectInfo) */ public void startReadingObjectInfoWithOid(OID oid, NonNativeObjectInfo objectInfo) { if (oid == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID); } Object[] objects = (Object[]) readingObjectInfo.get(oid); if (objects == null) { // The key is the oid, the value is an array of 2 objects : // 1-the read count, 2-The object info // Here we are saying that the object with oid 'oid' is // being read for the first time Object[] values = { new Short((short) 1), objectInfo }; readingObjectInfo.put(oid, values); } else { // Here the object is already being read. It is necessary to // increase the read count short currentReadCount = ((Short) objects[0]).shortValue(); objects[0] = new Short((short) (currentReadCount + 1)); // Object is in memory, do not need to re-put in map. The key has // not changed // readingObjectInfo.put(oid, objects); } } /* * (non-Javadoc) * * @see org.neodatis.odb.core.session.ICache#endReadingObjectInfo(org.neodatis.odb.core.meta.NonNativeObjectInfo) */ public void endReadingObjectInfo(OID oid) { if (oid == null) { throw new ODBRuntimeException(NeoDatisError.CACHE_NULL_OID.addParameter(oid)); } Object[] values = (Object[]) readingObjectInfo.get(oid); if (values == null || values[0] == null) { throw new ODBRuntimeException(NeoDatisError.OBJECT_INFO_NOT_IN_TEMP_CACHE.addParameter(oid).addParameter("?")); } short readCount = ((Short) values[0]).shortValue(); if (readCount == 1) { readingObjectInfo.remove(oid); } else { values[0] = new Short((short) (readCount - 1)); // Object is in memory, do not need to re-put in map. The key has // not changed // readingObjectInfo.put(oid, values); } } public Map getOids() { return oids; } public void setOids(Map oids) { this.oids = oids; } public Map getObjectInfoPointersCacheFromOid() { return objectInfoPointersCacheFromOid; } public void setObjectInfoPointersCacheFromOid(Map objectInfoPointersCacheFromOid) { this.objectInfoPointersCacheFromOid = objectInfoPointersCacheFromOid; } public Map getObjects() { return objects; } public void setObjects(Map objects) { this.objects = objects; } public boolean objectWithIdIsInCommitedZone(OID oid) { return !unconnectedZoneOids.containsKey(oid); } public void addOIDToUnconnectedZone(OID oid) { if(!useCache){ return; } unconnectedZoneOids.put(oid, oid); } public static String usage() { StringBuffer buffer = new StringBuffer(); buffer.append("NbObj=").append(nbObjects); buffer.append(" - NbOIDs=").append(nbOids); buffer.append(" - NbObjPos=").append(nbObjectPositionByIds); buffer.append(" - NbOIHs=").append(nbOih); buffer.append(" - NbTransOIDs=").append(nbTransactionOids); buffer.append(" - Calls2getObjectWitOid=").append(nbCallsToGetObjectWithOid); buffer.append(" - Calls2getObjectInfoHeaderFromOid=").append(nbCallsToGetObjectInfoHeaderFromOid); buffer.append(" - Calls2getObjectInfoHeaderFromObject=").append(nbCallsToGetObjectInfoHeaderFromObject); return buffer.toString(); } protected boolean checkHeaderPosition() { return false; } }

Ads help maintain this website.