| /* |
| * 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); |
| |
| } |