blob: dfe2e33ba1607b2212bafd726c074a8158576641 [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.cocoon.caching.impl;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.caching.EventRegistry;
import org.apache.cocoon.caching.validity.Event;
import org.apache.commons.collections.map.MultiValueMap;
/**
* This abstract base implementation of <code>EventRegistry</code> stores
* the event-key mappings in a simple pair of <code>MultiMap</code>s. It
* leaves all persistence to its concrete subclasses. To protect against
* future confusing inheritance trees, all internal implementation of the
* event-key mapping mechanism is hidden from its subclasses. If future
* EventRegistry implementations desire to use a different event-key mapping
* strategy but share persistence code, this package should probably be
* refactored to employ composition rather than inheritance. For now,
* simplicity favors inheritance.
*
* @since 2.1
* @version $Id$
*/
public abstract class AbstractDoubleMapEventRegistry
extends AbstractLogEnabled
implements Initializable, EventRegistry, Disposable, ThreadSafe {
private boolean m_init_success = false;
// maps to store keys and events: always accessed through MultiValue decorators
private Map m_keyMap;
private Map m_eventMap;
// maps which decorate the maps above to give the MultiMap behavior
private MultiValueMap m_keyMultiMap;
private MultiValueMap m_eventMultiMap;
/**
* Registers (stores) a two-way mapping between this Event and this
* PipelineCacheKey for later retrieval.
*
* @param e The event to
* @param key key
*/
public void register(Event e, Serializable key) {
synchronized(this) {
m_keyMultiMap.put(key,e);
m_eventMultiMap.put(e,key);
}
}
/**
* Remove all registered data.
*/
public void clear() {
synchronized(this) {
m_keyMultiMap.clear();
m_eventMultiMap.clear();
}
}
/**
* Retrieve all pipeline keys mapped to this event.
*/
public Serializable[] keysForEvent(Event e) {
synchronized(this) {
Collection coll = (Collection)m_eventMultiMap.get(e);
if (coll==null || coll.isEmpty()) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("The event map returned empty");
}
return null;
} else {
return (Serializable[])coll.toArray(new Serializable[coll.size()]);
}
}
}
/**
* Return all pipeline keys mapped to any event
*/
public Serializable[] allKeys() {
synchronized(this) {
Set keys = this.m_keyMultiMap.keySet();
return (Serializable[])keys.toArray(
new Serializable[keys.size()]);
}
}
/**
* When a CachedResponse is removed from the Cache, any entries
* in the event mapping must be cleaned up.
*/
public void removeKey(Serializable key) {
synchronized(this) {
Collection coll = (Collection)m_keyMultiMap.get(key);
if (coll==null) {
return;
}
// get the iterator over all matching PCK keyed
// entries in the key-indexed MMap.
Iterator it = coll.iterator();
while (it.hasNext()) {
/* remove all entries in the event-indexed map where this
* PCK key is the value.
*/
Object o = it.next();
if (o != null) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Removing from event mapping: " + o.toString());
}
m_eventMultiMap.remove(o,key);
}
}
// remove all entries in the key-indexed map where this PCK key
// is the key -- confused yet?
m_keyMultiMap.remove(key);
}
}
/**
* Recover state by de-serializing the data wrapper. If this fails
* a new empty mapping is initialized and the Cache is signalled of
* the failure so it can clean up.
*/
public void initialize() throws Exception {
if (recover()) {
m_init_success = true;
}
}
/**
* Delegate persistence to subclasses then clean up resources.
*/
public void dispose() {
EventRegistryDataWrapper ecdw = wrapRegistry();
persist(ecdw);
m_keyMultiMap = null;
m_eventMultiMap = null;
m_keyMap = null;
m_eventMap = null;
}
/**
* @return true if persistent state existed and was recovered successfully.
*/
public boolean wasRecoverySuccessful() {
return m_init_success;
}
protected EventRegistryDataWrapper wrapRegistry() {
EventRegistryDataWrapper ecdw = new EventRegistryDataWrapper();
ecdw.setupMaps(this.m_keyMap, this.m_eventMap);
return ecdw;
}
protected void unwrapRegistry(EventRegistryDataWrapper ecdw) {
this.m_eventMap = ecdw.get_eventMap();
this.m_keyMap = ecdw.get_keyMap();
createMultiMaps();
}
protected final void createBlankCache() {
// TODO: don't hardcode initial size
this.m_eventMap = new HashMap(1000);
this.m_keyMap = new HashMap(1000);
createMultiMaps();
}
protected void createMultiMaps() {
this.m_eventMultiMap = MultiValueMap.decorate(m_eventMap,HashSet.class);
this.m_keyMultiMap = MultiValueMap.decorate(m_keyMap,HashSet.class);
}
/**
* An EventRegistry must recover its persisted data. Failed
* recovery must be signaled so that the Cache will know not to
* serve potentially stale content. Of course, at first start up
* failed recovery is a normal state.
*
* @return boolean to signal success or failure of recovery.
*/
protected abstract boolean recover();
/**
* An EventRegistry must persist its data.
*/
protected abstract void persist(EventRegistryDataWrapper wrapper);
}