
org.cristalise.kernel.persistency.ClusterStorageManager Maven / Gradle / Ivy
/**
* This file is part of the CRISTAL-iSE kernel.
* Copyright (c) 2001-2015 The CRISTAL Consortium. All rights reserved.
*
* This library 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 3 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; with out 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* http://www.fsf.org/licensing/licenses/lgpl.html
*/
package org.cristalise.kernel.persistency;
import static org.cristalise.kernel.persistency.ClusterType.HISTORY;
import static org.cristalise.kernel.persistency.ClusterType.JOB;
import static org.cristalise.kernel.persistency.ClusterType.VIEWPOINT;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import org.cristalise.kernel.common.ObjectNotFoundException;
import org.cristalise.kernel.common.PersistencyException;
import org.cristalise.kernel.entity.C2KLocalObject;
import org.cristalise.kernel.entity.agent.JobList;
import org.cristalise.kernel.entity.proxy.ProxyMessage;
import org.cristalise.kernel.events.History;
import org.cristalise.kernel.lookup.AgentPath;
import org.cristalise.kernel.lookup.ItemPath;
import org.cristalise.kernel.persistency.outcome.Viewpoint;
import org.cristalise.kernel.process.Gateway;
import org.cristalise.kernel.process.auth.Authenticator;
import org.cristalise.kernel.querying.Query;
import org.cristalise.kernel.utils.Logger;
import org.cristalise.kernel.utils.SoftCache;
import org.cristalise.kernel.utils.WeakCache;
/**
* Instantiates ClusterStorages listed in properties file All read/write requests to storage pass through this object,
* which can query the capabilities of each declared storage, and channel requests accordingly. Transaction based.
* It also has a memoryCache to increase performance, use 'Storage.disableCache=true' to disable it.
*/
public class ClusterStorageManager {
HashMap allStores = new HashMap();
String[] clusterPriority = new String[0];
HashMap> clusterWriters = new HashMap>();
HashMap> clusterReaders = new HashMap>();
ArrayList transactionalStores = new ArrayList();
// we don't need a soft cache for the top level cache - the proxies and entities clear that when reaped
HashMap> memoryCache = new HashMap>();
/**
* Initialises all ClusterStorage handlers listed by class name in the property "ClusterStorages"
* This property is usually process specific, and so should be in the server/client.conf and not the connect file.
*
* @param auth the Authenticator to be used to initialise all the handlers
*/
public ClusterStorageManager(Authenticator auth) throws PersistencyException {
Object clusterStorageProp = Gateway.getProperties().getObject("ClusterStorage");
if (clusterStorageProp == null || "".equals(clusterStorageProp)) {
throw new PersistencyException("ClusterStorageManager.init() - no ClusterStorages defined. No persistency!");
}
ArrayList rootStores;
if (clusterStorageProp instanceof String) {
rootStores = instantiateStores((String)clusterStorageProp);
}
else if (clusterStorageProp instanceof ArrayList>) {
ArrayList> propStores = (ArrayList>)clusterStorageProp;
rootStores = new ArrayList();
clusterPriority = new String[propStores.size()];
for (Object thisStore : propStores) {
if (thisStore instanceof ClusterStorage)
rootStores.add((ClusterStorage)thisStore);
else
throw new PersistencyException("Supplied ClusterStorage "+thisStore.toString()+" was not an instance of ClusterStorage");
}
}
else {
throw new PersistencyException("Unknown class of ClusterStorage property: "+clusterStorageProp.getClass().getName());
}
int clusterNo = 0;
for (ClusterStorage newStorage : rootStores) {
try {
newStorage.open(auth);
}
catch (PersistencyException ex) {
Logger.error(ex);
throw new PersistencyException("ClusterStorageManager.init() - Error initialising storage handler " + newStorage.getClass().getName() + ": " + ex.getMessage());
}
Logger.msg(5, "ClusterStorageManager.init() - Cluster storage " + newStorage.getClass().getName() + " initialised successfully.");
allStores.put(newStorage.getId(), newStorage);
clusterPriority[clusterNo++] = newStorage.getId();
if (newStorage instanceof TransactionalClusterStorage) transactionalStores.add((TransactionalClusterStorage)newStorage);
}
clusterReaders.put(ClusterType.ROOT, rootStores); // all storages are queried for clusters at the root level
}
public ArrayList instantiateStores(String allClusters) throws PersistencyException {
ArrayList rootStores = new ArrayList();
StringTokenizer tok = new StringTokenizer(allClusters, ",");
clusterPriority = new String[tok.countTokens()];
while (tok.hasMoreTokens()) {
ClusterStorage newStorage = null;
String newStorageClass = tok.nextToken();
try {
if (!newStorageClass.contains(".")) newStorageClass = "org.cristalise.storage."+newStorageClass;
newStorage = (ClusterStorage)(Class.forName(newStorageClass).newInstance());
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
throw new PersistencyException("ClusterStorageManager.init() - The cluster storage handler class "+newStorageClass+" could not be found.");
}
rootStores.add(newStorage);
}
return rootStores;
}
public void close() {
for (ClusterStorage thisStorage : allStores.values()) {
try {
thisStorage.close();
}
catch (PersistencyException ex) {
Logger.error(ex);
}
}
}
/**
* Check which storage can execute the given query
*
* @param language the language of the query
* @return the found store or null
*/
private ClusterStorage findStorageForQuery(String language) {
for (String element : clusterPriority) {
ClusterStorage store = allStores.get(element);
if (store.checkQuerySupport(language) ) return store;
}
return null;
}
/**
* Returns the loaded storage that declare that they can handle writing or reading the specified cluster name (e.g.
* Collection, Property) Must specify if the request is a read or a write.
*
* @param clusterType
* @param forWrite whether the request is for write or read
* @return the list of usable storages
*/
private ArrayList findStorages(ClusterType clusterType, boolean forWrite) {
// choose the right cache for readers or writers
HashMap> cache;
if (forWrite) cache = clusterWriters;
else cache = clusterReaders;
// check to see if we've been asked to do this before
if (cache.containsKey(clusterType)) return cache.get(clusterType);
// not done yet, we'll have to query them all
Logger.msg(7, "ClusterStorageManager.findStorages() - finding storage for "+clusterType+" forWrite:"+forWrite);
ArrayList useableStorages = new ArrayList();
for (String element : clusterPriority) {
ClusterStorage thisStorage = allStores.get(element);
short requiredSupport = forWrite ? ClusterStorage.WRITE : ClusterStorage.READ;
if ((thisStorage.queryClusterSupport(clusterType) & requiredSupport) == requiredSupport) {
Logger.msg(7, "ClusterStorageManager.findStorages() - Got "+thisStorage.getName());
useableStorages.add(thisStorage);
}
}
cache.put(clusterType, useableStorages);
return useableStorages;
}
/**
* Executes the Query
*
* @param query the Query to be executed
* @return the xml result of the query
*/
public String executeQuery(Query query) throws PersistencyException {
ClusterStorage reader = findStorageForQuery(query.getLanguage());
if (reader != null) return reader.executeQuery(query);
else throw new PersistencyException("No storage was found supporting language:"+query.getLanguage()+" query:"+query.getName());
}
/**
* Retrieves the ids of the next level of a cluster
* Does not look in any currently open transactions.
*
* @param itemPath the current Item
* @param path the cluster path
* @return list of keys found in the cluster
*/
public String[] getClusterContents(ItemPath itemPath, String path) throws PersistencyException {
ArrayList contents = new ArrayList();
// get all readers
Logger.msg(8, "ClusterStorageManager.getClusterContents() - path:"+path);
ArrayList readers = findStorages(ClusterStorage.getClusterType(path), false);
// try each in turn until we get a result
for (ClusterStorage thisReader : readers) {
try {
String[] thisArr = thisReader.getClusterContents(itemPath, path);
if (thisArr != null) {
for (int j = 0; j < thisArr.length; j++)
if (!contents.contains(thisArr[j])) {
Logger.msg(9, "ClusterStorageManager.getClusterContents() - "+thisReader.getName()+" reports "+thisArr[j]);
contents.add(thisArr[j]);
}
}
}
catch (PersistencyException e) {
Logger.msg(5, "ClusterStorageManager.getClusterContents() - reader " + thisReader.getName() +
" could not retrieve contents of " + itemPath + "/" + path + ": " + e.getMessage());
}
}
Logger.msg(8, "ClusterStorageManager.getClusterContents() - Returning "+contents.size()+" elements of path:"+path);
String[] retArr = new String[0];
retArr = contents.toArray(retArr);
return retArr;
}
/**
* Internal get method. Retrieves clusters from ClusterStorages & maintains the memory cache.
*
* There is a special case for Viewpoint. When path ends with /data it returns referenced Outcome instead of Viewpoint.
*
* @param itemPath current Iten
* @param path the cluster path
* @return the C2KObject located by path
*/
public C2KLocalObject get(ItemPath itemPath, String path) throws PersistencyException, ObjectNotFoundException {
// check cache first
Map sysKeyMemCache = memoryCache.get(itemPath);
if (sysKeyMemCache != null) {
synchronized(sysKeyMemCache) {
C2KLocalObject obj = sysKeyMemCache.get(path);
if (obj != null) {
Logger.msg(7, "ClusterStorageManager.get() - found "+itemPath+"/"+path+" in memcache");
return obj;
}
}
}
// special case for Viewpoint- When path ends with /data it returns referenced Outcome instead of Viewpoint
if (path.startsWith(VIEWPOINT.getName()) && path.endsWith("/data")) {
StringTokenizer tok = new StringTokenizer(path,"/");
if (tok.countTokens() == 4) { // to not catch viewpoints called 'data'
Viewpoint view = (Viewpoint)get(itemPath, path.substring(0, path.lastIndexOf("/")));
if (view != null) return view.getOutcome();
else return null;
}
}
C2KLocalObject result = null;
// deal out top level remote maps
if (path.indexOf('/') == -1) {
if (path.equals(HISTORY.getName())) {
result = new History(itemPath, null);
}
else if (path.equals(JOB.getName())) {
if (itemPath instanceof AgentPath) result = new JobList((AgentPath)itemPath, null);
else throw new ObjectNotFoundException("Items do not have job lists");
}
}
if (result == null) {
// else try each reader in turn until we find it
ArrayList readers = findStorages(ClusterStorage.getClusterType(path), false);
for (ClusterStorage thisReader : readers) {
try {
result = thisReader.get(itemPath, path);
Logger.msg(7, "ClusterStorageManager.get() - reading "+path+" from "+thisReader.getName() + " for item " + itemPath);
if (result != null) break; // got it!
}
catch (PersistencyException e) {
Logger.msg(7, "ClusterStorageManager.get() - reader "+thisReader.getName()+" could not retrieve "+itemPath+"/"+ path+": "+e.getMessage());
}
}
}
//No result was found after reading the list of ClusterStorages
if (result == null) {
throw new ObjectNotFoundException("ClusterStorageManager.get() - Path "+path+" not found in "+itemPath);
}
putInMemoryCache(itemPath, path, result);
return result;
}
public void put(ItemPath itemPath, C2KLocalObject obj) throws PersistencyException {
put(itemPath, obj, null);
}
/**
* Internal put method. Creates or overwrites a cluster in all writers. Used when committing transactions.
*/
public void put(ItemPath itemPath, C2KLocalObject obj, Object locker) throws PersistencyException {
String path = ClusterStorage.getPath(obj);
ArrayList writers = findStorages(ClusterStorage.getClusterType(path), true);
for (ClusterStorage thisWriter : writers) {
try {
Logger.msg(7, "ClusterStorageManager.put() - writing "+path+" to "+thisWriter.getName());
if (thisWriter instanceof TransactionalClusterStorage && locker != null)
((TransactionalClusterStorage)thisWriter).put(itemPath, obj, locker);
else
thisWriter.put(itemPath, obj);
}
catch (PersistencyException e) {
Logger.error("ClusterStorageManager.put() - writer " + thisWriter.getName() + " could not store " + itemPath + "/" + path + ": " + e.getMessage());
throw e;
}
}
putInMemoryCache(itemPath, path, obj);
// transmit proxy event
if(Gateway.getProxyServer() != null)
Gateway.getProxyServer().sendProxyEvent(new ProxyMessage(itemPath, path, ProxyMessage.ADDED));
else
Logger.warning("ClusterStorageManager.put() - ProxyServer is null - Proxies are not notified of this event");
}
/**
* Put the given C2KLocalObject of the Item in the memory cache. Use 'Storage.disableCache=true' to disable caching.
*
* @param itemPath the Item which data is going to be cached
* @param path the cluster patch of the object
* @param obj the C2KLocalObject to be cached
*/
private void putInMemoryCache(ItemPath itemPath, String path, C2KLocalObject obj) {
if (Gateway.getProperties().getBoolean("Storage.disableCache", false)) {
Logger.msg(8,"ClusterStorageManager.putInMemoryCache() - Cache is DISABLED");
return;
}
Map sysKeyMemCache;
if (memoryCache.containsKey(itemPath)) {
sysKeyMemCache = memoryCache.get(itemPath);
}
else {
boolean useWeak = Gateway.getProperties().getBoolean("Storage.useWeakCache", false);
Logger.msg(7,"ClusterStorageManager.putInMemoryCache() - Creating "+(useWeak ? "Weak" : "Strong")+" cache for item "+itemPath);
sysKeyMemCache = useWeak ? new WeakCache() : new SoftCache(0);
synchronized (memoryCache) {
memoryCache.put(itemPath, sysKeyMemCache);
}
}
synchronized(sysKeyMemCache) {
sysKeyMemCache.put(path, obj);
}
if (Logger.doLog(9)) dumpCacheContents(9);
}
public void remove(ItemPath itemPath, String path) throws PersistencyException {
remove(itemPath, path, null);
}
/**
* Deletes a cluster from all writers
*/
public void remove(ItemPath itemPath, String path, Object locker) throws PersistencyException {
ArrayList writers = findStorages(ClusterStorage.getClusterType(path), true);
for (ClusterStorage thisWriter : writers) {
try {
Logger.msg(7, "ClusterStorageManager.delete() - removing "+path+" from "+thisWriter.getName());
if (thisWriter instanceof TransactionalClusterStorage && locker != null)
((TransactionalClusterStorage)thisWriter).delete(itemPath, path, locker);
else
thisWriter.delete(itemPath, path);
}
catch (PersistencyException e) {
Logger.error("ClusterStorageManager.delete() - writer " + thisWriter.getName() + " could not delete " + itemPath + "/" + path + ": " + e.getMessage());
throw e;
}
}
if (memoryCache.containsKey(itemPath)) {
Map itemMemCache = memoryCache.get(itemPath);
synchronized (itemMemCache) {
itemMemCache.remove(path);
}
}
// transmit proxy event
if(Gateway.getProxyServer() != null)
Gateway.getProxyServer().sendProxyEvent(new ProxyMessage(itemPath, path, ProxyMessage.DELETED));
else
Logger.warning("ClusterStorageManager.remove() - ProxyServer is null - Proxies are not notified of this event");
}
public void clearCache(ItemPath itemPath, String path) {
Logger.msg(7, "ClusterStorageManager.clearCache() - removing "+itemPath+"/"+path);
if (memoryCache.containsKey(itemPath)) {
Map sysKeyMemCache = memoryCache.get(itemPath);
synchronized(sysKeyMemCache) {
for (Iterator iter = sysKeyMemCache.keySet().iterator(); iter.hasNext();) {
String thisPath = iter.next();
if (thisPath.startsWith(path)) {
Logger.msg(7, "ClusterStorageManager.clearCache() - removing "+itemPath+"/"+thisPath);
iter.remove();
}
}
}
}
}
public void clearCache(ItemPath itemPath) {
Logger.msg(5, "ClusterStorageManager.clearCache() - removing entire cache of "+itemPath);
if (memoryCache.containsKey(itemPath)) {
synchronized (memoryCache) {
if (Logger.doLog(6)) {
Map sysKeyMemCache = memoryCache.get(itemPath);
int size = sysKeyMemCache.size();
Logger.msg(6, "ClusterStorageManager.clearCache() - "+size+" objects to remove.");
}
memoryCache.remove(itemPath);
}
}
else
Logger.msg(6, "ClusterStorageManager.clearCache() - No objects cached");
}
public void clearCache() {
synchronized (memoryCache) {
memoryCache.clear();
}
Logger.msg(5, "ClusterStorageManager.clearCache() - cleared entire cache, "+memoryCache.size()+" entities.");
}
public void dumpCacheContents(int logLevel) {
if (!Logger.doLog(logLevel)) return;
synchronized(memoryCache) {
for (ItemPath itemPath : memoryCache.keySet()) {
Logger.msg(logLevel, "Cached Objects of Item " + itemPath);
Map sysKeyMemCache = memoryCache
.get(itemPath);
try {
synchronized (sysKeyMemCache) {
for (Object name : sysKeyMemCache.keySet()) {
String path = (String) name;
try {
Logger.msg(logLevel, " Path " + path + ": " + sysKeyMemCache.get(path).getClass().getName());
}
catch (NullPointerException e) {
Logger.msg(logLevel, " Path " + path + ": reaped");
}
}
}
}
catch (ConcurrentModificationException ex) {
Logger.msg(logLevel, "Cache modified - aborting");
}
}
Logger.msg(logLevel, "Total number of cached entities: "+memoryCache.size());
}
}
public void begin(Object locker) {
for (TransactionalClusterStorage thisStore : transactionalStores) {
thisStore.begin(locker);
}
}
public void commit(Object locker) throws PersistencyException {
for (TransactionalClusterStorage thisStore : transactionalStores) {
thisStore.commit(locker);
}
}
public void abort(Object locker) {
for (TransactionalClusterStorage thisStore : transactionalStores) {
thisStore.abort(locker);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy