/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and Cambridge Semantics Incorporated.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* File: $Source: /cvsroot/slrp/boca/com.ibm.adtech.boca.common/src/com/ibm/adtech/boca/rdb/layout/NodeLayoutCacheProxy.java,v $
* Created by: Stephen Evanchik <evanchik@us.ibm.com>
* Created on: 9/30/2005
* Revision: $Id: NodeLayoutCacheProxy.java 178 2007-07-31 14:22:33Z mroy $
*
* Contributors:
* IBM Corporation - initial API and implementation
* Cambridge Semantics Incorporated - Fork to Anzo
*******************************************************************************/
package org.openanzo.jdbc.layout;
import java.sql.Connection;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.openanzo.cache.ICache;
import org.openanzo.jdbc.utils.RdbException;
import org.openanzo.rdf.Value;
/**
* Wraps and caches the data accessed by a INodeLayout object. *
*
* @param <T>
* Type of Value being stored
* @author Joe Betz
* @author Stephen Evanchik <evanchik@us.ibm.com>
*/
public class NodeLayoutCacheProxy<T extends Value> implements INodeLayout<T>, ILayoutCache<T> {
private final INodeLayout<T> base;
private final ICache<Long, T> cache;
private final ICache<T, Long> idCache;
private final Map<Long, T> uncommitedByIdCache;
private final Map<T, Long> uncommitedByNodeCache;
/**
* Create NodeLayoutCacheProxy for the given base layout.
*
* @param base
* Base layout to add caching layer onto
* @param byIdCache
* ID cache for this layout
* @param byNodeCache
* Node cache for this layout
*/
protected NodeLayoutCacheProxy(INodeLayout<T> base, ICache<Long, T> cache, ICache<T, Long> idCache) {
this.base = base;
this.cache = cache;
this.idCache = idCache;
uncommitedByIdCache = Collections.synchronizedMap(new HashMap<Long, T>());
uncommitedByNodeCache = Collections.synchronizedMap(new HashMap<T, Long>());
}
private void clearUncommited() {
uncommitedByIdCache.clear();
uncommitedByNodeCache.clear();
}
public void clearCache() {
clearUncommited();
}
private void addToCache(Long id, T n) {
cache.put(id, n);
idCache.put(n, id);
}
private void addToUncommitedCache(Long id, T n) {
uncommitedByIdCache.put(id, n);
uncommitedByNodeCache.put(n, id);
}
public T cache(Long id, String value, Long modifierId, Connection connection) throws RdbException {
T node = base.convert(value, modifierId, connection);
cache.put(id, node);
idCache.put(node, id);
return node;
}
public Long store(T n, Connection connection, long transactionId) throws RdbException {
Long id = getIfCached(n);
if (id != null) {
return id;
}
id = base.store(n, connection, transactionId);
addToUncommitedCache(id, n);
return id;
}
public void commitUncommittedCache() {
for (Map.Entry<T, Long> entry : uncommitedByNodeCache.entrySet()) {
idCache.put(entry.getKey(), entry.getValue());
}
for (Map.Entry<Long, T> entry : uncommitedByIdCache.entrySet()) {
cache.put(entry.getKey(), entry.getValue());
}
clearUncommited();
}
public void clearUncommittedCache() {
uncommitedByNodeCache.clear();
uncommitedByIdCache.clear();
}
public boolean isCached(T n) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try {
return uncommitedByNodeCache.containsKey(n) || idCache.get(n) != null;
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
public boolean isCached(Long id) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try {
return uncommitedByIdCache.containsKey(id) || cache.get(id) != null;
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
public Long getIfCached(T value) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try {
Long id = uncommitedByNodeCache.get(value);
if (id == null) {
id = idCache.get(value);
}
return id;
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
public T getIfCached(Long id) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try {
T obj = uncommitedByIdCache.get(id);
if (obj == null) {
obj = cache.get(id);
}
return obj;
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
public Long fetchId(T n, Connection connection) throws RdbException {
Long id = getIfCached(n);
if (id != null) {
return id;
}
id = base.fetchId(n, connection);
if (id != null)
addToCache(id, n);
return id;
}
public T fetchValue(Long id, Connection connection) throws RdbException {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try {
T n = cache.get(id);
if (n != null) {
return n;
}
n = base.fetchValue(id, connection);
if (n == null) {
return null;
}
addToCache(id, n);
return n;
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
public Map<T, Long> resolveStoredNodes(Collection<T> nodes, boolean storeUnresolvedNodes, Connection connection, long transactionId) throws RdbException {
HashMap<T, Long> alreadyStored = new HashMap<T, Long>();
Set<T> notSeen = new HashSet<T>();
for (T node : nodes) {
Long id = getIfCached(node);
if (id != null) {
alreadyStored.put(node, id);
} else {
notSeen.add(node);
}
}
if (notSeen.size() > 0) {
Map<T, Long> resolved = base.resolveStoredNodes(notSeen, storeUnresolvedNodes, connection, transactionId);
for (Map.Entry<T, Long> entry : resolved.entrySet()) {
addToUncommitedCache(entry.getValue(), entry.getKey());
}
alreadyStored.putAll(resolved);
}
return alreadyStored;
}
public Map<Long, T> resolveStoredIds(Set<Long> ids, Connection connection) throws RdbException {
HashMap<Long, T> alreadyStored = new HashMap<Long, T>();
Set<Long> notSeen = new HashSet<Long>();
for (Long id : ids) {
T value = getIfCached(id);
if (value != null) {
alreadyStored.put(id, value);
addToCache(id, value);
} else {
notSeen.add(id);
}
}
if (notSeen.size() > 0) {
Map<Long, T> resolved = base.resolveStoredIds(notSeen, connection);
for (Map.Entry<Long, T> entry : resolved.entrySet()) {
addToUncommitedCache(entry.getKey(), entry.getValue());
}
alreadyStored.putAll(resolved);
}
return alreadyStored;
}
public NodeType getType() {
return base.getType();
}
public T convert(String value, Long modifierId, Connection connection) throws RdbException {
return base.convert(value, modifierId, connection);
}
public long abortReferencedIds(Connection connection, long transactionId) throws RdbException {
return base.abortReferencedIds(connection, transactionId);
}
public long commitReferencedIds(Connection connection, long transactionId) throws RdbException {
return base.commitReferencedIds(connection, transactionId);
}
public void setMaxLength(int length) {
this.base.setMaxLength(length);
}
}