package org.simantics.acorn.internal; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import org.simantics.acorn.ClusterManager; import org.simantics.acorn.exception.AcornAccessVerificationException; import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.db.Session; import org.simantics.db.exception.DatabaseException; import org.simantics.db.impl.ClusterBase; import org.simantics.db.impl.ClusterI; import org.simantics.db.impl.ClusterSupport; import org.simantics.db.impl.IClusterTable; import org.simantics.db.service.ClusterUID; import gnu.trove.map.hash.TIntObjectHashMap; public class ClusterSupport2 implements ClusterSupport, IClusterTable { final private static boolean DEBUG = false; public ClusterManager impl; public TIntObjectHashMap uidCache = new TIntObjectHashMap(); public ClusterSupport2(ClusterManager impl) { this.impl = impl; } @Override public int createClusterKeyByClusterUID(ClusterUID clusterUID, long clusterId) { throw new UnsupportedOperationException(); } @Override public ClusterBase getClusterByClusterUIDOrMake(ClusterUID clusterUID) { try { return impl.getClusterByClusterUIDOrMake(clusterUID); } catch (DatabaseException e) { e.printStackTrace(); return null; } } @Override public ClusterBase getClusterByClusterId(long clusterId) { throw new UnsupportedOperationException(); } @Override public ClusterBase getClusterByClusterKey(int clusterKey) { throw new UnsupportedOperationException(); } ReentrantReadWriteLock uidLock = new ReentrantReadWriteLock(); ReadLock uidRead = uidLock.readLock(); WriteLock uidWrite = uidLock.writeLock(); @Override public ClusterUID getClusterUIDByResourceKey(int resourceKey) throws DatabaseException { ClusterUID cuid; uidRead.lock(); cuid = uidCache.get(resourceKey >> 12); uidRead.unlock(); if(cuid != null) return cuid; uidWrite.lock(); cuid = uidCache.get(resourceKey >> 12); if(cuid == null) { cuid = impl.getClusterUIDByResourceKeyWithoutMutex(resourceKey); uidCache.put(resourceKey >> 12, cuid); } uidWrite.unlock(); return cuid; } @Override public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) { try { return impl.getClusterKeyByClusterUIDOrMakeWithoutMutex(clusterUID); } catch (IllegalAcornStateException | AcornAccessVerificationException e) { throw new RuntimeException(e); } } @Override public int getClusterKeyByClusterUIDOrMake(long id1, long id2) { throw new UnsupportedOperationException(); } @Override public ClusterBase getClusterByResourceKey(int resourceKey) { throw new UnsupportedOperationException(); // return impl.getClusterByResourceKey(resourceKey); } @Override public long getClusterIdOrCreate(ClusterUID clusterUID) { return impl.getClusterIdOrCreate(clusterUID); } @Override public void addStatement(Object cluster) { // nop } @Override public void cancelStatement(Object cluster) { // nop } @Override public void removeStatement(Object cluster) { // nop } @Override public void removeValue(Object cluster) { // nop } @Override public void setImmutable(Object cluster, boolean immutable) { // nop } @Override public void setDeleted(Object cluster, boolean deleted) { // TODO Auto-generated method stub } @Override public void cancelValue(Object cluster) { throw new UnsupportedOperationException(); } @Override public void setValue(Object cluster, long clusterId, byte[] bytes, int length) { // nop } @Override public void modiValue(Object _cluster, long clusterId, long voffset, int length, byte[] bytes, int offset) { // nop } @Override public void createResource(Object cluster, short resourceIndex, long clusterId) { // No op } @Override public void addStatementIndex(Object cluster, int resourceKey, ClusterUID clusterUID, byte op) { // No op } @Override public void setStreamOff(boolean setOff) { throw new UnsupportedOperationException(); } @Override public boolean getStreamOff() { return true; } private static class ResourceSegment { public long valueSize; public byte[] bytes; ResourceSegment(long valueSize, byte[] bytes) { this.valueSize = valueSize; this.bytes = bytes; } } public ResourceSegment getResourceSegment(int resourceIndex, ClusterUID clusterUID, long offset, short size) throws DatabaseException { if (DEBUG) System.out.println("DEBUG: getResourceSegment ri=" + resourceIndex + " cid=" + clusterUID + " offset=" + offset + " size=" + size); try { org.simantics.db.Database.Session.ResourceSegment t = impl.getResourceSegment(clusterUID.asBytes(), resourceIndex, offset, size); return new ResourceSegment(t.getValueSize(), t.getSegment()); } catch (AcornAccessVerificationException | IllegalAcornStateException e) { throw new DatabaseException(e); } } protected byte[] getValueBig(ClusterBase cluster, int resourceIndex, int offset, int length) throws DatabaseException { assert(offset == 0); assert(length == 0); ClusterUID clusterUID = cluster.clusterUID; try { return impl.getResourceFile(clusterUID.asBytes(), resourceIndex); } catch (AcornAccessVerificationException | IllegalAcornStateException e) { throw new DatabaseException(e); } } protected InputStream getValueStreamBig(ClusterBase cluster, final int resourceIndex, int offset, int length) throws DatabaseException { final ClusterUID clusterUID = cluster.clusterUID; if (DEBUG) System.out.println("DEBUG: getResourceFile ri=" + resourceIndex + " cid=" + clusterUID + " off=" + offset + " len=" + length); final int IMAX = 0xFFFF; short slen = (short)Math.min(length != 0 ? length : IMAX, IMAX); final ResourceSegment s = getResourceSegment(resourceIndex, clusterUID, offset, slen); if (s.valueSize < 0) throw new DatabaseException("Failed to get value for resource index=" + resourceIndex + " cluster=" + clusterUID + " off=" + offset + " len=" + length + " (1)."); int ilen = (int)slen & 0xFFFF; assert(s.bytes.length <= ilen); if (0 == length) { if (s.valueSize > Integer.MAX_VALUE) throw new DatabaseException("Failed to get value for resource index=" + resourceIndex + " cluster=" + clusterUID + " off=" + offset + " len=" + length + ". Value size=" + s.valueSize + " (2)."); length = (int)s.valueSize; } long rSize = s.valueSize - offset; if (rSize < length) throw new DatabaseException("Failed to get value for resource index=" + resourceIndex + " cluster=" + clusterUID + " off=" + offset + " len=" + length + ". Value size=" + s.valueSize + " (3)."); else if (length <= IMAX) return new ByteArrayInputStream(s.bytes); final int finalLength = length; return new InputStream() { int left = finalLength; long valueOffset = 0; int offset = 0; ResourceSegment _s = s; @Override public int read() throws IOException { if(left <= 0) return -1; if(offset == _s.bytes.length) { short slen = (short)Math.min(left, IMAX); valueOffset += _s.bytes.length; try { _s = getResourceSegment(resourceIndex, clusterUID, valueOffset, slen); } catch (DatabaseException e) { throw new IOException(e); } offset = 0; } left--; int result = _s.bytes[offset++]; if(result < 0) result += 256; return result; } }; } @Override public InputStream getValueStreamEx(int resourceIndex, long clusterId) throws DatabaseException { ClusterBase cluster = impl.getClusterByClusterUIDOrMakeProxy(ClusterUID.make(0, clusterId)); return getValueStreamBig(cluster, resourceIndex, 0, 0); } @Override public byte[] getValueEx(int resourceIndex, long clusterId) throws DatabaseException { ClusterBase cluster = impl.getClusterByClusterUIDOrMakeProxy(ClusterUID.make(0, clusterId)); return getValueBig(cluster, resourceIndex, 0, 0); } @Override public byte[] getValueEx(int resourceIndex, long clusterId, long voffset, int length) throws DatabaseException { throw new UnsupportedOperationException(); } @Override public long getValueSizeEx(int resourceIndex, long clusterId) throws DatabaseException { throw new UnsupportedOperationException(); } @Override public int wait4RequestsLess(int limit) throws DatabaseException { throw new UnsupportedOperationException(); } @Override public Session getSession() { return null; } @Override public IClusterTable getClusterTable() { return this; } @Override public T getClusterByClusterUIDOrMakeProxy(ClusterUID clusterUID) { try { return (T)impl.getClusterByClusterUIDOrMakeProxy(clusterUID); } catch (DatabaseException e) { e.printStackTrace(); return null; } } @Override public T getClusterProxyByResourceKey(int resourceKey) { try { return impl.getClusterProxyByResourceKey(resourceKey); } catch (DatabaseException e) { e.printStackTrace(); return null; } } @Override public int getClusterKeyByUID(long id1, long id2) throws DatabaseException { return impl.getClusterKeyByUID(id1, id2); } }