org.jboss.iiop.rmi.WorkCacheManager Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.iiop.rmi;
import java.lang.ref.SoftReference;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.HashMap;
import java.util.WeakHashMap;
/**
* Instances of this class cache the most complex analyse types.
*
* The analyse types cached are:
*
* InterfaceAnalysis
for interfaces.
* ValueAnalysis
for value types.
* ExceptionAnalysis
for exceptions.
*
*
* Besides caching work already done, this caches work in progress,
* as we need to know about this to handle cyclic graphs of analyses.
* When a thread re-enters the getAnalysis()
metohd, an
* unfinished analysis will be returned if the same thread is already
* working on this analysis.
*
* @author Ole Husgaard
* @version $Revision: 81018 $
*/
class WorkCacheManager
{
// Constants -----------------------------------------------------
// Attributes ----------------------------------------------------
// Static --------------------------------------------------------
private static final org.jboss.logging.Logger logger =
org.jboss.logging.Logger.getLogger(WorkCacheManager.class);
// Constructors --------------------------------------------------
/**
* Create a new work cache manager.
*
* @param cls The class of the analysis type we cache here.
*/
WorkCacheManager(Class cls)
{
logger.debug("Class: " + cls.getName());
// Find the constructor and initializer.
try {
constructor = cls.getDeclaredConstructor(new Class[]{Class.class});
initializer = cls.getDeclaredMethod("doAnalyze", null);
} catch (NoSuchMethodException ex) {
throw new IllegalArgumentException("Bad Class: " + ex.toString());
}
workDone = new WeakHashMap();
workInProgress = new HashMap();
}
// Package private -----------------------------------------------
/**
* Returns an analysis.
* If the calling thread is currently doing an analysis of this
* class, an unfinished analysis is returned.
*/
ContainerAnalysis getAnalysis(Class cls)
throws RMIIIOPViolationException
{
ContainerAnalysis ret;
synchronized (this) {
ret = lookupDone(cls);
if (ret != null)
return ret;
// is it work-in-progress?
InProgress inProgress = (InProgress)workInProgress.get(cls);
if (inProgress != null) {
if (inProgress.thread == Thread.currentThread())
return inProgress.analysis; // return unfinished
// Do not wait for the other thread: We may deadlock
// Double work is better that deadlock...
}
ret = createWorkInProgress(cls);
}
// Do the work
doTheWork(cls, ret);
// We did it
synchronized (this) {
workInProgress.remove(cls);
workDone.put(cls, new SoftReference(ret));
notifyAll();
}
return ret;
}
// Private -------------------------------------------------------
/**
* The analysis constructor of our analysis class.
* This constructor takes a single argument of type Class
.
*/
Constructor constructor;
/**
* The analysis initializer of our analysis class.
* This method takes no arguments, and is named doAnalyze.
*/
Method initializer;
/**
* This maps the classes of completely done analyses to soft
* references of their analysis.
*/
Map workDone;
/**
* This maps the classes of analyses in progress to their
* analysis.
*/
Map workInProgress;
/**
* Lookup an analysis in the fully done map.
*/
private ContainerAnalysis lookupDone(Class cls)
{
SoftReference ref = (SoftReference)workDone.get(cls);
if (ref == null)
return null;
ContainerAnalysis ret = (ContainerAnalysis)ref.get();
if (ret == null)
workDone.remove(cls); // clear map entry if soft ref. was cleared.
return ret;
}
/**
* Create new work-in-progress.
*/
private ContainerAnalysis createWorkInProgress(Class cls)
{
ContainerAnalysis analysis;
try {
analysis = (ContainerAnalysis)constructor.newInstance(new Object[]{cls});
} catch (InstantiationException ex) {
throw new RuntimeException(ex.toString());
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex.toString());
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.toString());
}
workInProgress.put(cls, new InProgress(analysis, Thread.currentThread()));
return analysis;
}
private void doTheWork(Class cls, ContainerAnalysis ret)
throws RMIIIOPViolationException
{
try {
initializer.invoke(ret, new Object[]{});
} catch (Throwable t) {
synchronized (this) {
workInProgress.remove(cls);
}
if (t instanceof InvocationTargetException) // unwrap
t = ((InvocationTargetException)t).getTargetException();
if (t instanceof RMIIIOPViolationException)
throw (RMIIIOPViolationException)t;
if (t instanceof RuntimeException)
throw (RuntimeException)t;
if (t instanceof Error)
throw (Error)t;
throw new RuntimeException(t.toString());
}
}
/**
* A simple aggregate of work-in-progress, and the thread doing the work.
*/
private static class InProgress
{
ContainerAnalysis analysis;
Thread thread;
InProgress(ContainerAnalysis analysis, Thread thread)
{
this.analysis = analysis;
this.thread = thread;
}
}
}