/* * 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: * <ul> * <li><code>InterfaceAnalysis</code> for interfaces.</li> * <li><code>ValueAnalysis</code> for value types.</li> * <li><code>ExceptionAnalysis</code> for exceptions.</li> * </ul> * * 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 <code>getAnalysis()</code> metohd, an * unfinished analysis will be returned if the same thread is already * working on this analysis. * * @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a> * @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 <code>Class</code>. */ 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; } } }