/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ package Sirius.server; import org.apache.log4j.Logger; import java.io.Serializable; import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * DOCUMENT ME! * * @author martin.scholl@cismet.de * @version $Revision$, $Date$ */ public abstract class Shutdown extends AbstractShutdownable implements Serializable { //~ Static fields/initializers --------------------------------------------- private static final transient Logger LOG = Logger.getLogger(Shutdown.class); public static final int PRIORITY_LATEST = Integer.MAX_VALUE; public static final int PRIORITY_LATER = Integer.MAX_VALUE / 2; public static final int PRIORITY_NORMAL = 0; public static final int PRIORITY_EARLIER = Integer.MIN_VALUE / 2; public static final int PRIORITY_EARLIEST = Integer.MIN_VALUE; //~ Instance fields -------------------------------------------------------- private final Map<Integer, Set<Shutdownable>> shutdowns; //~ Constructors ----------------------------------------------------------- /** * Creates a new Shutdown object. */ protected Shutdown() { shutdowns = new HashMap<Integer, Set<Shutdownable>>(); } //~ Methods ---------------------------------------------------------------- /** * Adds all shutdownable fields of the subclass to the shutdown list using normal priority. It uses reflection to * get private declared fields and sets them accessible. Thus the shutdown sequence can be harmed if the * {@link SecurityManager} does not allow access to the subclass' private fields. */ private void addShutdownableFields() { final Field[] fields = this.getClass().getDeclaredFields(); for (final Field field : fields) { try { field.setAccessible(true); final Object fieldValue = field.get(this); if (fieldValue instanceof Shutdownable) { final Shutdownable sd = (Shutdownable)fieldValue; if ((sd != null) && (getPriority(sd) == null)) { if (LOG.isDebugEnabled()) { LOG.debug("adding shutdownable field: " + sd); // NOI18N } addShutdown(sd); } } } catch (final Exception ex) { LOG.warn("shutdown probably incomplete: cannot add shutdownable field: " + field, ex); // NOI18N } } } /** * DOCUMENT ME! * * @param o DOCUMENT ME! * * @return DOCUMENT ME! */ public static Shutdown createShutdown(final Object o) { final Shutdown shutdown = new Shutdown() { }; final Field[] fields = o.getClass().getDeclaredFields(); for (final Field field : fields) { try { field.setAccessible(true); final Object fieldValue = field.get(o); if (fieldValue instanceof Shutdownable) { final Shutdownable sd = (Shutdownable)fieldValue; if ((sd != null) && (shutdown.getPriority(sd) == null)) { if (LOG.isDebugEnabled()) { LOG.debug("adding shutdownable field: " + sd); // NOI18N } shutdown.addShutdown(sd); } } } catch (final Exception ex) { LOG.warn("shutdown probably incomplete: cannot add shutdownable field: " + field, ex); // NOI18N } } return shutdown; } @Override protected void internalShutdown() throws ServerExitError { addShutdownableFields(); final Integer[] priorities = shutdowns.keySet().toArray(new Integer[shutdowns.size()]); Arrays.sort(priorities); for (final Integer priority : priorities) { for (final Shutdownable shutdownable : shutdowns.get(priority)) { if (LOG.isDebugEnabled()) { LOG.debug("shutting down: " + shutdownable); // NOI18N } if (!shutdownable.isDown()) { shutdownable.shutdown(); } } } shutdowns.clear(); } /** * DOCUMENT ME! * * @param sd DOCUMENT ME! */ protected final void addShutdown(final Shutdownable sd) { addShutdown(PRIORITY_NORMAL, sd); } /** * DOCUMENT ME! * * @param priority DOCUMENT ME! * @param sd DOCUMENT ME! */ protected final synchronized void addShutdown(final int priority, final Shutdownable sd) { if ((sd == null) || sd.isDown() || sd.equals(this)) { return; } removeShutdown(sd); if (shutdowns.containsKey(priority)) { shutdowns.get(priority).add(sd); } else { final Set<Shutdownable> shutdownables = new HashSet<Shutdownable>(5); shutdownables.add(sd); shutdowns.put(priority, shutdownables); } } /** * DOCUMENT ME! * * @param sd DOCUMENT ME! * * @return DOCUMENT ME! */ private Integer getPriority(final Shutdownable sd) { if (sd == null) { return null; } synchronized (shutdowns) { for (final Integer priority : shutdowns.keySet()) { for (final Shutdownable shutdownable : shutdowns.get(priority)) { if (sd.equals(shutdownable)) { return priority; } } } } return null; } /** * DOCUMENT ME! * * @param sd DOCUMENT ME! */ protected final synchronized void removeShutdown(final Shutdownable sd) { if ((sd == null) || isDown()) { return; } final Integer priority = getPriority(sd); if (priority != null) { shutdowns.get(priority).remove(sd); } } }