/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.persistence; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import com.servoy.j2db.util.SortedList; /** * @author sebster */ public class RootObjectCache { private final AbstractRepository repository; private final HashMap<Integer, CacheRecord> rootObjectsById; private final HashMap rootObjectsByName; class RootObjectKey { String name; int objectTypeId; public RootObjectKey(String name, int objectTypeId) { this.name = name; this.objectTypeId = objectTypeId; } @Override public boolean equals(Object other) { if (other instanceof RootObjectKey) { final RootObjectKey otherKey = (RootObjectKey)other; return name.equals(otherKey.name) && objectTypeId == otherKey.objectTypeId; } return false; } @Override public int hashCode() { return name.hashCode() * 29 + objectTypeId; } } class CacheRecord { RootObjectMetaData rootObjectMetaData; HashMap<Integer, IRootObject> rootObjects; @Override public String toString() { return RepositoryHelper.getObjectTypeName(rootObjectMetaData.getObjectTypeId()) + ": " + rootObjectMetaData.toString() + " count: " + rootObjects.size(); //$NON-NLS-1$ } } RootObjectCache(AbstractRepository repository, Collection metaDatas) { this.repository = repository; rootObjectsById = new HashMap<Integer, CacheRecord>(); rootObjectsByName = new HashMap(); // Initialize the cache. Iterator iterator = metaDatas.iterator(); while (iterator.hasNext()) { add((RootObjectMetaData)iterator.next(), false); } } synchronized void add(RootObjectMetaData metaData, boolean allowUpdate) { Integer intId = new Integer(metaData.getRootObjectId()); CacheRecord cacheRecord = allowUpdate ? rootObjectsById.get(intId) : null; if (cacheRecord == null) { cacheRecord = new CacheRecord(); cacheRecord.rootObjects = new HashMap<Integer, IRootObject>(); rootObjectsById.put(intId, cacheRecord); rootObjectsByName.put(new RootObjectKey(metaData.getName(), metaData.getObjectTypeId()), cacheRecord); } else { for (IRootObject ro : cacheRecord.rootObjects.values()) { ((AbstractRootObject)ro).setMetaData(metaData); } } cacheRecord.rootObjectMetaData = metaData; } public synchronized void cacheRootObject(IRootObject rootObject) throws RepositoryException { CacheRecord cacheRecord = getCacheRecordEnsureNotNull(rootObject.getID()); Integer release = new Integer(rootObject.getReleaseNumber()); if (cacheRecord.rootObjects.get(release) != null) { // Root object is already cached, this is a bug. throw new RepositoryException( "duplicate cache of " + RepositoryHelper.getObjectTypeName(rootObject.getTypeID()) + " with name '" + rootObject.getName() + "'"); //$NON-NLS-1$ //$NON-NLS-2$ } cacheRecord.rootObjects.put(release, rootObject); } public synchronized void renameRootObject(int rootObjectId, String name) throws RepositoryException { RootObjectMetaData metaData = getRootObjectMetaData(rootObjectId); RootObjectKey newKey = new RootObjectKey(name, metaData.getObjectTypeId()); if (rootObjectsByName.containsKey(newKey)) { throw new RepositoryException("root object with name='" + name + "' and type=" + metaData.getObjectTypeId() + " already exists"); } RootObjectKey oldKey = new RootObjectKey(metaData.getName(), metaData.getObjectTypeId()); Object cacheRecord = rootObjectsByName.remove(oldKey); metaData.setName(name); rootObjectsByName.put(newKey, cacheRecord); } public IRootObject getActiveRootObject(int rootObjectId) throws RepositoryException { return getRootObject(rootObjectId, 0); } public IRootObject getActiveRootObject(String name, int objectTypeId) throws RepositoryException { RootObjectMetaData rod = getRootObjectMetaData(name, objectTypeId); if (rod != null) { return getActiveRootObject(rod.getRootObjectId()); } return null; } public IRootObject getRootObject(String name, int objectTypeId, int release) throws RepositoryException { RootObjectMetaData rod = getRootObjectMetaData(name, objectTypeId); if (rod != null) { return getRootObject(rod.getRootObjectId(), release); } return null; } public synchronized boolean isRootObjectCached(String name, int objectTypeId, int release) throws RepositoryException { RootObjectMetaData rod = getRootObjectMetaData(name, objectTypeId); if (rod != null) { CacheRecord cacheRecord = getCacheRecord(rod.getRootObjectId()); if (cacheRecord != null) { if (release == 0) { release = cacheRecord.rootObjectMetaData.getActiveRelease(); } else if (release == -1) { release = cacheRecord.rootObjectMetaData.getLatestRelease(); } Integer key = new Integer(release); IRootObject rootObject = cacheRecord.rootObjects.get(key); return rootObject != null; } } return false; } synchronized IRootObject getRootObject(int rootObjectId, int release) throws RepositoryException { CacheRecord cacheRecord = getCacheRecord(rootObjectId); if (cacheRecord == null) { return null; } if (release == 0) { release = cacheRecord.rootObjectMetaData.getActiveRelease(); } else if (release == -1) { release = cacheRecord.rootObjectMetaData.getLatestRelease(); } Integer key = new Integer(release); IRootObject rootObject = cacheRecord.rootObjects.get(key); if (rootObject == null) { rootObject = repository.loadRootObject(cacheRecord.rootObjectMetaData, release); cacheRecord.rootObjects.put(key, rootObject); } return rootObject; } // synchronized void setActiveRelease(int rootObjectId, int activeRelease) throws RepositoryException // { // loadCacheRecordEnsureNotNull(rootObjectId).rootObjectMetaData.setActiveRelease(activeRelease); // } // // synchronized void setLatestRelease(int rootObjectId, int latestRelease) throws RepositoryException // { // loadCacheRecordEnsureNotNull(rootObjectId).rootObjectMetaData.setLatestRelease(latestRelease); // } public synchronized int getLatestRelease(int rootObjectId) throws RepositoryException { return getCacheRecordEnsureNotNull(rootObjectId).rootObjectMetaData.getLatestRelease(); } public synchronized int getActiveRelease(int rootObjectId) throws RepositoryException { return getCacheRecordEnsureNotNull(rootObjectId).rootObjectMetaData.getActiveRelease(); } public synchronized RootObjectMetaData getRootObjectMetaData(int rootObjectId) throws RepositoryException { CacheRecord cacheRecord = getCacheRecord(rootObjectId); return cacheRecord != null ? cacheRecord.rootObjectMetaData : null; } synchronized RootObjectMetaData getRootObjectMetaData(String name, int objectTypeId) throws RepositoryException { CacheRecord cacheRecord = getCacheRecord(name, objectTypeId); return cacheRecord != null ? cacheRecord.rootObjectMetaData : null; } synchronized RootObjectMetaData[] getRootObjectMetaDatas() throws RepositoryException { RootObjectMetaData[] metaDatas = new RootObjectMetaData[rootObjectsById.size()]; Iterator<CacheRecord> iterator = rootObjectsById.values().iterator(); int i = 0; while (iterator.hasNext()) { metaDatas[i++] = iterator.next().rootObjectMetaData; } return metaDatas; } synchronized RootObjectMetaData[] getRootObjectMetaDatasForType(int objectTypeId) throws RepositoryException { List list = new SortedList(NameComparator.INSTANCE); Iterator<CacheRecord> iterator = rootObjectsById.values().iterator(); while (iterator.hasNext()) { RootObjectMetaData metaData = iterator.next().rootObjectMetaData; if (metaData.getObjectTypeId() == objectTypeId) { list.add(metaData); } } RootObjectMetaData[] filtered = new RootObjectMetaData[list.size()]; return (RootObjectMetaData[])list.toArray(filtered); } synchronized void removeRootObject(int rootObjectId) throws RepositoryException { Integer key = new Integer(rootObjectId); flushRootObject(rootObjectId); CacheRecord cacheRecord = rootObjectsById.get(key); if (cacheRecord != null) { rootObjectsById.remove(key); rootObjectsByName.remove(new RootObjectKey(cacheRecord.rootObjectMetaData.getName(), cacheRecord.rootObjectMetaData.getObjectTypeId())); } } synchronized void flushRootObject(int rootObjectId) throws RepositoryException { Integer key = new Integer(rootObjectId); CacheRecord cacheRecord = rootObjectsById.get(key); if (cacheRecord != null) { flushRootObjectMap(cacheRecord.rootObjects); } } synchronized void flushRootObjectRelease(int rootObjectId, int release) throws RepositoryException { CacheRecord cacheRecord = rootObjectsById.get(new Integer(rootObjectId)); if (cacheRecord != null) { if (release == 0) { release = cacheRecord.rootObjectMetaData.getActiveRelease(); } else if (release == -1) { release = cacheRecord.rootObjectMetaData.getLatestRelease(); } Integer key = new Integer(release); IRootObject rootObject = cacheRecord.rootObjects.get(key); if (rootObject != null) { rootObject.getChangeHandler().rootObjectIsFlushed(); cacheRecord.rootObjects.remove(key); } } } synchronized void flush() throws RepositoryException { Iterator<CacheRecord> iterator = rootObjectsById.values().iterator(); while (iterator.hasNext()) { CacheRecord cacheRecord = iterator.next(); flushRootObjectMap(cacheRecord.rootObjects); } } private void flushRootObjectMap(HashMap<Integer, IRootObject> rootObjectMap) throws RepositoryException { Iterator<IRootObject> iterator = rootObjectMap.values().iterator(); while (iterator.hasNext()) { IRootObject rootObject = iterator.next(); ChangeHandler ch = rootObject.getChangeHandler(); if (ch != null) { ch.rootObjectIsFlushed(); } iterator.remove(); } } private CacheRecord getCacheRecord(int rootObjectId) throws RepositoryException { Integer key = new Integer(rootObjectId); return rootObjectsById.get(key); } private CacheRecord getCacheRecordEnsureNotNull(int rootObjectId) throws RepositoryException { CacheRecord cacheRecord = getCacheRecord(rootObjectId); if (cacheRecord == null) { throw new RepositoryException("root object with id " + rootObjectId + " does not exist in the repository"); } return cacheRecord; } private CacheRecord getCacheRecord(String name, int objectTypeId) throws RepositoryException { if (name == null) return null; RootObjectKey key = new RootObjectKey(name, objectTypeId); return (CacheRecord)rootObjectsByName.get(key); } }