blob: 9e7c732b14e56a491e1ce784e2fd07db134cc72b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.datacache;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.event.RemoteCommitEvent;
import org.apache.openjpa.event.RemoteCommitListener;
import org.apache.openjpa.lib.conf.Configurable;
import org.apache.openjpa.lib.conf.Configuration;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.concurrent.AbstractConcurrentEventManager;
/**
* Abstract {@link DataCache} implementation that provides various
* statistics, logging, and timeout functionality common across cache
* implementations.
*
* @author Patrick Linskey
* @author Abe White
*/
public abstract class AbstractDataCache
extends AbstractConcurrentEventManager
implements DataCache, Configurable {
private static final BitSet EMPTY_BITSET = new BitSet(0);
private static final Localizer s_loc =
Localizer.forPackage(AbstractDataCache.class);
/**
* The configuration set by the system.
*/
protected OpenJPAConfiguration conf;
/**
* The log to use.
*/
protected Log log;
private String _name = null;
private boolean _closed = false;
private String _schedule = null;
public String getName() {
return _name;
}
public void setName(String name) {
_name = name;
}
public String getEvictionSchedule() {
return _schedule;
}
public void setEvictionSchedule(String s) {
_schedule = s;
}
public void initialize(DataCacheManager manager) {
if (_schedule != null && !"".equals(_schedule)) {
DataCacheScheduler scheduler = manager.getDataCacheScheduler();
if (scheduler != null)
scheduler.scheduleEviction(this, _schedule);
}
}
public void commit(Collection additions, Collection newUpdates,
Collection existingUpdates, Collection deletes) {
// remove all objects in deletes list
removeAllInternal(deletes);
// next, add all the new additions
putAllInternal(additions);
putAllInternal(newUpdates);
// possibly add the existing updates, depending on the
// semantics of the cache, as dictated by recacheUpdates()
if (recacheUpdates())
putAllInternal(existingUpdates);
if (log.isTraceEnabled()) {
Collection addIds = new ArrayList(additions.size());
Collection upIds = new ArrayList(newUpdates.size());
Collection exIds = new ArrayList(existingUpdates.size());
for (Iterator iter = additions.iterator(); iter.hasNext();)
addIds.add(((DataCachePCData) iter.next()).getId());
for (Iterator iter = newUpdates.iterator(); iter.hasNext();)
upIds.add(((DataCachePCData) iter.next()).getId());
for (Iterator iter = existingUpdates.iterator(); iter.hasNext();)
exIds.add(((DataCachePCData) iter.next()).getId());
log.trace(s_loc.get("cache-commit",
new Object[]{ addIds, upIds, exIds, deletes }));
}
}
public boolean contains(Object key) {
DataCachePCData o = getInternal(key);
if (o != null && o.isTimedOut()) {
o = null;
removeInternal(key);
if (log.isTraceEnabled())
log.trace(s_loc.get("cache-timeout", key));
}
return o != null;
}
public BitSet containsAll(Collection keys) {
if (keys.isEmpty())
return EMPTY_BITSET;
BitSet set = new BitSet(keys.size());
int i = 0;
for (Iterator iter = keys.iterator(); iter.hasNext(); i++)
if (contains(iter.next()))
set.set(i);
return set;
}
public DataCachePCData get(Object key) {
DataCachePCData o = getInternal(key);
if (o != null && o.isTimedOut()) {
o = null;
removeInternal(key);
if (log.isTraceEnabled())
log.trace(s_loc.get("cache-timeout", key));
}
if (log.isTraceEnabled()) {
if (o == null)
log.trace(s_loc.get("cache-miss", key));
else
log.trace(s_loc.get("cache-hit", key));
}
return o;
}
public DataCachePCData put(DataCachePCData data) {
DataCachePCData o = putInternal(data.getId(), data);
if (log.isTraceEnabled())
log.trace(s_loc.get("cache-put", data.getId()));
return (o == null || o.isTimedOut()) ? null : o;
}
public void update(DataCachePCData data) {
if (recacheUpdates())
putInternal(data.getId(), data);
}
public DataCachePCData remove(Object key) {
DataCachePCData o = removeInternal(key);
if (o != null && o.isTimedOut())
o = null;
if (log.isTraceEnabled()) {
if (o == null)
log.trace(s_loc.get("cache-remove-miss", key));
else
log.trace(s_loc.get("cache-remove-hit", key));
}
return o;
}
public BitSet removeAll(Collection keys) {
if (keys.isEmpty())
return EMPTY_BITSET;
BitSet set = new BitSet(keys.size());
int i = 0;
for (Iterator iter = keys.iterator(); iter.hasNext(); i++)
if (remove(iter.next()) != null)
set.set(i);
return set;
}
/**
* Remove the objects of the given class from the cache.
*/
public void removeAll(Class cls, boolean subClasses) {
removeAllInternal(cls, subClasses);
}
public boolean pin(Object key) {
boolean bool = pinInternal(key);
if (log.isTraceEnabled()) {
if (bool)
log.trace(s_loc.get("cache-pin-hit", key));
else
log.trace(s_loc.get("cache-pin-miss", key));
}
return bool;
}
public BitSet pinAll(Collection keys) {
if (keys.isEmpty())
return EMPTY_BITSET;
BitSet set = new BitSet(keys.size());
int i = 0;
for (Iterator iter = keys.iterator(); iter.hasNext(); i++)
if (pin(iter.next()))
set.set(i);
return set;
}
public void pinAll(Class cls, boolean subs) {
if (log.isWarnEnabled())
log.warn(s_loc.get("cache-class-pin", getName()));
}
public boolean unpin(Object key) {
boolean bool = unpinInternal(key);
if (log.isTraceEnabled()) {
if (bool)
log.trace(s_loc.get("cache-unpin-hit", key));
else
log.trace(s_loc.get("cache-unpin-miss", key));
}
return bool;
}
public BitSet unpinAll(Collection keys) {
if (keys.isEmpty())
return EMPTY_BITSET;
BitSet set = new BitSet(keys.size());
int i = 0;
for (Iterator iter = keys.iterator(); iter.hasNext(); i++)
if (unpin(iter.next()))
set.set(i);
return set;
}
public void unpinAll(Class cls, boolean subs) {
if (log.isWarnEnabled())
log.warn(s_loc.get("cache-class-unpin", getName()));
}
public void clear() {
clearInternal();
if (log.isTraceEnabled())
log.trace(s_loc.get("cache-clear", getName()));
}
public void close() {
close(true);
}
protected void close(boolean clear) {
if (!_closed) {
if (clear)
clearInternal();
_closed = true;
}
}
public boolean isClosed() {
return _closed;
}
public void addExpirationListener(ExpirationListener listen) {
addListener(listen);
}
public boolean removeExpirationListener(ExpirationListener listen) {
return removeListener(listen);
}
public String toString() {
return "[" + super.toString() + ":" + _name + "]";
}
/**
* This method is part of the {@link RemoteCommitListener} interface. If
* your cache subclass relies on OpenJPA for clustering support, make it
* implement <code>RemoteCommitListener</code>. This method will take
* care of invalidating entries from remote commits.
*/
public void afterCommit(RemoteCommitEvent event) {
if (_closed)
return;
if (event.getPayloadType() == RemoteCommitEvent.PAYLOAD_EXTENTS) {
removeAllTypeNamesInternal(event.getUpdatedTypeNames());
removeAllTypeNamesInternal(event.getDeletedTypeNames());
} else {
// drop all the committed OIDs, excepting brand
// new OIDs. brand new OIDs either won't be in
// the cache, or if they are, will be more up to date
removeAllInternal(event.getUpdatedObjectIds());
removeAllInternal(event.getDeletedObjectIds());
}
}
/**
* Invoke when a key is removed from this cache. Propagates the
* expiration event on to all expiration listeners registered
* with this class.
*/
protected void keyRemoved(Object key, boolean expired) {
// Notify any expiration listeners of the expiration.
if (hasListeners())
fireEvent(new ExpirationEvent(this, key, expired));
if (expired && log.isTraceEnabled())
log.trace(s_loc.get("cache-expired", key));
}
/**
* Return <code>true</code> if updates to data already in the
* cache (either in {@link #commit} or the {@link #update})
* should be put back into the cache. Returns false by default.
*/
protected boolean recacheUpdates() {
return false;
}
/**
* Return the object for the given oid.
*/
protected abstract DataCachePCData getInternal(Object oid);
/**
* Add the given object to the cache, returning the old object under the
* given oid.
*/
protected abstract DataCachePCData putInternal(Object oid,
DataCachePCData pc);
/**
* All all of the given objects to the cache.
*/
protected void putAllInternal(Collection pcs) {
DataCachePCData pc;
for (Iterator iter = pcs.iterator(); iter.hasNext();) {
pc = (DataCachePCData) iter.next();
putInternal(pc.getId(), pc);
}
}
/**
* Remove the object under the given oid from the cache.
*/
protected abstract DataCachePCData removeInternal(Object oid);
/**
* Evict objects in cache by class.
*/
protected abstract void removeAllInternal(Class cls, boolean subclasses);
/**
* Remove all objects under the given oids from the cache.
*/
protected void removeAllInternal(Collection oids) {
for (Iterator iter = oids.iterator(); iter.hasNext();)
removeInternal(iter.next());
}
/**
* Remove all objects of the given class names from the cache.
*/
protected void removeAllTypeNamesInternal(Collection classNames) {
Collection classes = Caches.addTypesByName(conf, classNames, null);
if (classes == null)
return;
Class cls;
for (Iterator iter = classes.iterator(); iter.hasNext();) {
cls = (Class) iter.next();
if (log.isTraceEnabled())
log.trace(s_loc.get("cache-removeclass", cls.getName()));
removeAllInternal(cls, false);
}
}
/**
* Clear the cache.
*/
protected abstract void clearInternal();
/**
* Pin an object to the cache.
*/
protected abstract boolean pinInternal(Object oid);
/**
* Unpin an object from the cache.
*/
protected abstract boolean unpinInternal(Object oid);
// ---------- Configurable implementation ----------
public void setConfiguration(Configuration conf) {
this.conf = (OpenJPAConfiguration) conf;
this.log = conf.getLog(OpenJPAConfiguration.LOG_DATACACHE);
}
public void startConfiguration() {
}
public void endConfiguration() {
if (_name == null)
setName(NAME_DEFAULT);
}
// ---------- AbstractEventManager implementation ----------
protected void fireEvent(Object event, Object listener) {
ExpirationListener listen = (ExpirationListener) listener;
ExpirationEvent ev = (ExpirationEvent) event;
try {
listen.onExpire(ev);
} catch (Exception e) {
if (log.isWarnEnabled())
log.warn(s_loc.get("exp-listener-ex"), e);
}
}
/**
* Returns the objects for the given key List.
*/
public Map getAll(List keys) {
Map resultMap = new HashMap(keys.size());
for(int i=0; i<keys.size(); i++)
resultMap.put(keys.get(i), get(keys.get(i)));
return resultMap;
}
}