package com.c2kernel.persistency; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import com.c2kernel.common.ObjectNotFoundException; import com.c2kernel.entity.C2KLocalObject; import com.c2kernel.entity.proxy.*; import com.c2kernel.entity.proxy.EntityProxyObserver; import com.c2kernel.lookup.EntityPath; import com.c2kernel.process.Gateway; import com.c2kernel.utils.Logger; /** * Maps a storage cluster onto a java.util.Map * * @author Andrew Branson * $Revision: 1.22 $ * $Date: 2006/03/03 13:52:21 $ * * Copyright (C) 2003 CERN - European Organization for Nuclear Research * All rights reserved. */ public class RemoteMap implements C2KLocalObject, Map { protected static final boolean KEYS = false; protected static final boolean VALUES = true; private int mID=-1; private String mName; protected int mSysKey; private String mPath = ""; protected String[] keys = null; Object keyLock = new Object(); protected C2KLocalObject[] values = null; TransactionManager storage; EntityProxyObserver listener; EntityProxy source; Object mLocker; // if this remote map will participate in a transaction public RemoteMap(int sysKey, String path, Object locker) { mSysKey = sysKey; mLocker = locker; // split the path into path/name int lastSlash = path.lastIndexOf("/"); mName = path.substring(lastSlash+1); if (lastSlash>0) mPath = path.substring(0,lastSlash); // see if the name is also a suitable id try { mID = Integer.parseInt(mName); } catch (NumberFormatException e) {} storage = Gateway.getStorage(); listener = new EntityProxyObserver() { public void add(C2KLocalObject obj) { synchronized (keyLock) { if (keys == null) return; boolean found = false; for (int i=0; i lastID) lastID = thisID; } catch (NumberFormatException e) { Logger.warning("RemoteMap.getLastID() - Cluster contained invalid id: "+allIds[i]); } } Logger.msg(7, "RemoteMap.getLastID() - last id in "+mPath+mName+" of "+mSysKey+" is "+lastID); return lastID; } } // c2kLocalObject methods public void setID(int id) { mID = id; } public int getID() { return mID; } public void setName(String name) { mName = name; } public String getName() { return mName; } /** * Cannot be stored */ public String getClusterType() { return null; } /** * @see java.util.Map#clear() */ public synchronized void clear() { keys = null; } /** * @see java.util.Map#containsKey(Object) */ public synchronized boolean containsKey(Object key) { getKeys(); for (int i = 0; i < keys.length; i++) { if (key.equals(keys[i])) return true; } return false; } /** * This must retrieve all the values until a match is made. * Very expensive, but if you must, you must. * @see java.util.Map#containsValue(Object) */ public synchronized boolean containsValue(Object value) { getKeys(); synchronized(keyLock) { if (values == null) values = new C2KLocalObject[keys.length]; for (int i = 0; i < keys.length; i++) { try { if (values[i] == null) values[i] = storage.get(mSysKey, mPath+mName+"/"+keys[i], mLocker); if (value.equals(values[i])) return true; } catch (ClusterStorageException ex) { Logger.error(ex); } catch (ObjectNotFoundException e) { Logger.error(e); } } } return false; } /** * @see java.util.Map#entrySet() */ public synchronized Set entrySet() { return new RemoteMap.RemoteSet(this, KEYS); } /** * @see java.util.Map#get(Object) */ public synchronized Object get(Object key) { getKeys(); synchronized(keyLock) { if (values == null) values = new C2KLocalObject[keys.length]; try { for (int i = 0; i < keys.length; i++) { if (key.equals(keys[i])) { if (values[i] == null) values[i] = storage.get(mSysKey, mPath+mName+"/"+keys[i], mLocker); return values[i]; } } } catch (ClusterStorageException e) { Logger.error(e); } catch (ObjectNotFoundException e) { Logger.error(e); } } return null; } /** * @see java.util.Map#isEmpty() */ public synchronized boolean isEmpty() { return getKeys().length==0; } /** * @see java.util.Map#keySet() */ public synchronized Set keySet() { return new RemoteMap.RemoteSet(this, KEYS); } /** * Inserts the given object into the storage * the key is ignored - it can be fetched from the value. * @see java.util.Map#put(Object, Object) */ public synchronized Object put(Object key, Object value) { try { C2KLocalObject newValue = (C2KLocalObject)value; synchronized(keyLock) { storage.put(mSysKey, newValue, mLocker); keys = null; values = null; } } catch (ClusterStorageException e) { Logger.error(e); return null; } catch (ClassCastException e) { Logger.error("RemoteMap.put() - value was not a localobject, it was a "+value.getClass().getName()); return null; } return value; } /** * @see java.util.Map#putAll(Map) */ public synchronized void putAll(Map t) { for (Iterator iter = t.keySet().iterator(); iter.hasNext();) { Object key = iter.next(); put(key, t.get(key)); } } /** * @see java.util.Map#remove(Object) */ public synchronized Object remove(Object key) { try { synchronized(keyLock) { storage.remove(mSysKey, mPath+mName+"/"+key, mLocker); keys = null; values = null; } } catch (ClusterStorageException e) { Logger.error(e); } return null; } /** * @see java.util.Map#size() */ public synchronized int size() { return getKeys().length; } /** * @see java.util.Map#values() */ public synchronized Collection values() { return new RemoteMap.RemoteSet(this, VALUES); } /** * Basic implementation of Set and Collection to bridge to the Iterator * Disallows all writes. */ private class RemoteSet extends AbstractSet { RemoteMap mParent; boolean mMode; public RemoteSet(RemoteMap parent, boolean mode) { mParent = parent; mMode = mode; } // no modifications allowed public boolean add(Object o) { throw new UnsupportedOperationException(); } public boolean addAll(Collection c) { throw new UnsupportedOperationException(); } public void clear() { throw new UnsupportedOperationException(); } public boolean remove(Object o) { throw new UnsupportedOperationException(); } public boolean removeAll(Collection c) { throw new UnsupportedOperationException(); } public boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } public Iterator iterator() { return new RemoteIterator(mParent, mMode); } public int size() { return mParent.size(); } } /** * Iterator view on RemoteMap data. Doesn't preload anything. * REVISIT: Will go strange if the RemoteMap is modified. Detect this and throw ConcurrentMod ex */ private class RemoteIterator implements Iterator { RemoteMap mParent; boolean mMode; String[] keyArr; int pos; public RemoteIterator(RemoteMap parent, boolean mode) { mParent = parent; mMode = mode; keyArr = mParent.getKeys(); } public boolean hasNext() { return (pos