Java tutorial
package org.apache.torque.manager; /* * 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. */ import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.collections.FastArrayList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jcs.JCS; import org.apache.jcs.access.GroupCacheAccess; import org.apache.jcs.access.exception.CacheException; import org.apache.torque.Torque; import org.apache.torque.TorqueException; import org.apache.torque.om.ObjectKey; import org.apache.torque.om.Persistent; /** * This class contains common functionality of a Manager for * instantiating OM's. * * @param <T> the class of the database object managed by this class. * @author <a href="mailto:jmcnally@collab.net">John McNally</a> * @version $Id: AbstractBaseManager.java 1379317 2012-08-31 06:56:48Z tfischer $ */ public abstract class AbstractBaseManager<T extends Persistent> implements Serializable { /** serial version */ private static final long serialVersionUID = 509798299473481305L; /** the log */ protected static final Log log = LogFactory.getLog(AbstractBaseManager.class); /** used to cache the om objects. cache is set by the region property */ protected transient GroupCacheAccess cache; /** method results cache */ protected MethodResultCache mrCache; /** The OM class that the service will instantiate. */ private Class<T> omClass; /** The name of the OM class that the service will instantiate. */ private String className; /** The cache region used for JCS. */ private String region; /** Whether the cache manager has already registered its cache Listeners. */ private boolean isNew = true; /** The fields which are valid fields of interest for a listener. */ protected Map<String, ?> validFields; /** The listeners for this manager. */ protected Map<String, FastArrayList> listenersMap = new HashMap<String, FastArrayList>(); /** * Get the Class instance * * @return the om class */ protected Class<T> getOMClass() { return omClass; } /** * Set the Class that will be instantiated by this manager * * @param omClass the om class */ protected void setOMClass(Class<T> omClass) { this.omClass = omClass; } /** * Get a fresh instance of an om * * @return an instance of the om class * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ protected T getOMInstance() throws TorqueException { try { return omClass.newInstance(); } catch (InstantiationException e) { throw new TorqueException("Could not instantiate " + getClassName(), e); } catch (IllegalAccessException e) { throw new TorqueException("Could not access " + getClassName(), e); } } /** * Get the classname to instantiate for getInstance() * * @return value of className. */ public String getClassName() { return className; } /** * Set the classname to instantiate for getInstance() * @param v Value to assign to className. * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ @SuppressWarnings("unchecked") public void setClassName(String v) throws TorqueException { this.className = v; try { Class<?> clazz = Class.forName(getClassName()); if (Persistent.class.isAssignableFrom(clazz)) { setOMClass((Class<T>) clazz); } else { throw new TorqueException(getClassName() + " does not implement the interface Persistent"); } } catch (ClassNotFoundException cnfe) { throw new TorqueException("Could not load " + getClassName(), cnfe); } } /** * Return an instance of an om based on the id * * @param id the primary key of the object * @return the object from persistent storage or from cache * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ protected T getOMInstance(ObjectKey id) throws TorqueException { return getOMInstance(id, true); } /** * Return an instance of an om based on the id * * @param key the primary key of the object * @param fromCache true if the object should be retrieved from cache * @return the object from persistent storage or from cache * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ protected T getOMInstance(ObjectKey key, boolean fromCache) throws TorqueException { T om = null; if (fromCache) { om = cacheGet(key); } if (om == null) { om = retrieveStoredOM(key); if (fromCache) { putInstanceImpl(om); } } return om; } /** * Get an object from cache * * @param key the primary key of the object * @return the object from cache */ @SuppressWarnings("unchecked") protected T cacheGet(Serializable key) { T om = null; if (cache != null) { synchronized (this) { om = (T) cache.get(key); } } return om; } /** * Clears the cache * * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ protected void clearImpl() throws TorqueException { if (cache != null) { try { cache.clear(); } catch (CacheException ce) { throw new TorqueException("Could not clear cache due to internal JCS error.", ce); } } } /** * Disposes of the cache. This triggers a shutdown of the connected cache * instances. This method should only be used during shutdown of Torque. The * manager instance will not cache anymore after this call. */ public void dispose() { if (cache != null) { cache.dispose(); cache = null; } } /** * Remove an object from the cache * * @param key the cache key for the object * @return the object one last time * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ @SuppressWarnings("unchecked") protected T removeInstanceImpl(Serializable key) throws TorqueException { T oldOm = null; if (cache != null) { try { synchronized (this) { oldOm = (T) cache.get(key); cache.remove(key); } } catch (CacheException ce) { throw new TorqueException("Could not remove from cache due to internal JCS error", ce); } } return oldOm; } /** * Put an object into the cache * * @param om the object * @return if an object with the same key already is in the cache * this object will be returned, else null * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ protected T putInstanceImpl(T om) throws TorqueException { ObjectKey key = om.getPrimaryKey(); return putInstanceImpl(key, om); } /** * Put an object into the cache * * @param key the cache key for the object * @param om the object * @return if an object with this key already is in the cache * this object will be returned, else null * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ @SuppressWarnings("unchecked") protected T putInstanceImpl(Serializable key, T om) throws TorqueException { if (getOMClass() != null && !getOMClass().isInstance(om)) { throw new TorqueException(om + "; class=" + om.getClass().getName() + "; id=" + om.getPrimaryKey() + " cannot be cached with " + getOMClass().getName() + " objects"); } T oldOm = null; if (cache != null) { try { synchronized (this) { oldOm = (T) cache.get(key); cache.put(key, om); } } catch (CacheException ce) { throw new TorqueException("Could not cache due to internal JCS error", ce); } } return oldOm; } /** * Retrieve an object from persistent storage * * @param id the primary key of the object * @return the object * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ protected abstract T retrieveStoredOM(ObjectKey id) throws TorqueException; /** * Gets a list of om's based on id's. * * @param ids a <code>ObjectKey[]</code> value * @return a <code>List</code> value * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ protected List<T> getOMs(ObjectKey[] ids) throws TorqueException { return getOMs(Arrays.asList(ids)); } /** * Gets a list of om's based on id's. * * @param ids a <code>List</code> of <code>ObjectKey</code>'s * @return a <code>List</code> value * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ protected List<T> getOMs(List<? extends ObjectKey> ids) throws TorqueException { return getOMs(ids, true); } /** * Gets a list of om's based on id's. * * @param ids a <code>List</code> of <code>ObjectKey</code>'s * @return a <code>List</code> value * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ protected List<T> getOMs(List<? extends ObjectKey> ids, boolean fromCache) throws TorqueException { List<T> oms = null; if (ids != null && ids.size() > 0) { // start a new list where we will replace the id's with om's Map<ObjectKey, T> omsMap = new HashMap<ObjectKey, T>(); List<ObjectKey> newIds = new ArrayList<ObjectKey>(ids.size()); for (ObjectKey key : ids) { T om = null; if (fromCache) { om = cacheGet(key); } if (om == null) { newIds.add(key); } else { omsMap.put(key, om); } } if (newIds.size() > 0) { List<T> newOms = retrieveStoredOMs(newIds); for (T om : newOms) { omsMap.put(om.getPrimaryKey(), om); if (fromCache) { putInstanceImpl(om); } } } oms = new ArrayList<T>(ids.size()); for (ObjectKey key : ids) { oms.add(omsMap.get(key)); } } return oms; } /** * Gets a list of om's based on id's. * This method must be implemented in the derived class * * @param ids a <code>List</code> of <code>ObjectKey</code>'s * @return a <code>List</code> value * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ protected abstract List<T> retrieveStoredOMs(List<? extends ObjectKey> ids) throws TorqueException; /** * Get the cache region used for JCS. * * @return the cache region used for JCS. */ public String getRegion() { return region; } /** * Set the cache region used for JCS. * * @param v Value to assign to region. * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ public void setRegion(String v) throws TorqueException { this.region = v; try { if (Torque.getConfiguration().getBoolean(Torque.CACHE_KEY, false)) { cache = JCS.getInstance(getRegion()); mrCache = new MethodResultCache(cache); } else { mrCache = new NoOpMethodResultCache(cache); } } catch (CacheException e) { throw new TorqueException("Cache could not be initialized", e); } if (cache == null) { log.info("Cache could not be initialized for region: " + v); } } /** * @return The cache instance. */ public synchronized MethodResultCache getMethodResultCache() { if (isNew) { registerAsListener(); isNew = false; } return mrCache; } /** * NoOp version. Managers should override this method to notify other * managers that they are interested in CacheEvents. */ protected void registerAsListener() { // empty } /** * * @param listener A new listener for cache events. */ public void addCacheListenerImpl(CacheListener<? extends Persistent> listener) { List<String> keys = listener.getInterestedFields(); for (String key : keys) { // Peer.column names are the fields if (validFields != null && validFields.containsKey(key)) { FastArrayList listeners = listenersMap.get(key); if (listeners == null) { listeners = createSubsetList(key); } boolean isListenerNew = true; Iterator<?> j = listeners.iterator(); while (j.hasNext()) { Object listener2 = ((WeakReference<?>) j.next()).get(); // if (listener2 == null) // { // // do a little cleanup while checking for dupes // // not thread-safe, not likely to be many nulls // // but should revisit // //j.remove(); // } // else if (listener2 == listener) { isListenerNew = false; break; } } if (isListenerNew) { listeners.add(new WeakReference<CacheListener<? extends Persistent>>(listener)); } } } } /** * * @param key * @return A subset of the list identified by <code>key</code>. */ private synchronized FastArrayList createSubsetList(String key) { FastArrayList list = null; if (listenersMap.containsKey(key)) { list = listenersMap.get(key); } else { list = new FastArrayList(); list.setFast(true); listenersMap.put(key, list); } return list; } /** * * @param listeners * @param oldOm * @param om */ protected <TT extends Persistent> void notifyListeners(List<WeakReference<CacheListener<TT>>> listeners, TT oldOm, TT om) { if (listeners != null) { synchronized (listeners) { Iterator<WeakReference<CacheListener<TT>>> i = listeners.iterator(); while (i.hasNext()) { CacheListener<TT> listener = i.next().get(); if (listener == null) { // remove reference as its object was cleared i.remove(); } else { if (oldOm == null) { // object was added listener.addedObject(om); } else { // object was refreshed listener.refreshedObject(om); } } } } } } /** * helper methods for the Serializable interface * * @param out * @throws IOException */ private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.defaultWriteObject(); } /** * Helper methods for the <code>Serializable</code> interface. * * @param in The stream to read a <code>Serializable</code> from. * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); // initialize the cache try { if (region != null) { setRegion(region); } } catch (Exception e) { log.error("Cache could not be initialized for region '" + region + "' after deserialization"); } } }