/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util.collection;

import java.util.Collection;
import java.util.AbstractCollection;
import java.util.Iterator;

import java.lang.ref.ReferenceQueue;

import org.jboss.util.SoftObject;
import org.jboss.util.Objects;

/**
 * A wrapper around a Collection which translates added objects
 * into {@link SoftObject} references, allowing the VM to garbage collect
 * objects in the collection when memory is low.
 *
 * @version <tt>$Revision$</tt>
 * @author  Jason Dillon
 */
@SuppressWarnings("unchecked")
public class CachedCollection
   extends AbstractCollection
{
   /** Reference queue */
   protected final ReferenceQueue queue = new ReferenceQueue();

   /** Wrapped collection */
   protected final Collection collection;

   /**
    * Construct a CachedCollection.
    *
    * @param collection    Collection to wrap.
    */
   public CachedCollection(final Collection collection) {
      this.collection = collection;
   }

   /**
    * Returns an iterator over the elements contained in this collection.
    *
    * @return An iterator over the elements contained in this collection.
    */
   public Iterator iterator() {
      maintain();
      return new MyIterator(collection.iterator());
   }

   /**
    * Returns the size of the collection.
    *
    * @return  The number of elements in the collection.
    */
   public int size() {
      maintain();
      return collection.size();
   }

   /**
    * Add an object to the collection.
    *
    * @param obj  Object (or null to add to the collection.
    * @return     True if object was added.
    */
   public boolean add(final Object obj) {
      maintain();

      SoftObject soft = SoftObject.create(obj, queue);

      return collection.add(soft);
   }

   /**
    * Maintains the collection by removing garbage collected objects.
    */
   private void maintain() {
      SoftObject obj;
      int count = 0;

      while ((obj = (SoftObject)queue.poll()) != null) {
         count++;
         collection.remove(obj);
      }

      if (count != 0) {
         // some temporary debugging fluff
         System.err.println("vm reclaimed " + count + " objects");
      }
   }

   /////////////////////////////////////////////////////////////////////////
   //                       De-Referencing Iterator                       //
   /////////////////////////////////////////////////////////////////////////

   /**
    * A dereferencing iterator.
    */
   private final class MyIterator
      implements Iterator
   {
      private final Iterator iter;

      public MyIterator(final Iterator iter) {
         this.iter = iter;
      }

      public boolean hasNext() {
         maintain();
         return iter.hasNext();
      }

      private Object nextObject() {
         Object obj = iter.next();

         return Objects.deref(obj);
      }

      public Object next() {
         maintain();
         return nextObject();
      }

      public void remove() {
         maintain();
         iter.remove();
      }
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util;

import java.lang.ref.SoftReference;
import java.lang.ref.ReferenceQueue;

/**
 * Convenience class to wrap an <tt>Object</tt> into a <tt>SoftReference</tt>.
 *
 *
Modified from <tt>java.util.WeakHashMap.WeakKey</tt>.
 *
 * @version <tt>$Revision$</tt>
 * @author  Jason Dillon
 */
@SuppressWarnings("unchecked")
public final class SoftObject
   extends SoftReference
{
   /** The hash code of the nested object */
   protected final int hashCode;

   /**
    * Construct a <tt>SoftObject</tt>.
    *
    * @param obj  Object to reference.
    */
   public SoftObject(final Object obj) {
      super(obj);
      hashCode = obj.hashCode();
   }

   /**
    * Construct a <tt>SoftObject</tt>.
    *
    * @param obj     Object to reference.
    * @param queue   Reference queue.
    */
   public SoftObject(final Object obj, final ReferenceQueue queue) {
      super(obj, queue);
      hashCode = obj.hashCode();
   }

   /**
    * Check the equality of an object with this.
    *
    * @param obj  Object to test equality with.
    * @return     True if object is equal.
    */
   public boolean equals(final Object obj) {
      if (obj == this) return true;

      if (obj != null && obj.getClass() == getClass()) {
         SoftObject soft = (SoftObject)obj;

         Object a = this.get();
         Object b = soft.get();
         if (a == null || b == null) return false;
         if (a == b) return true;

         return a.equals(b);
      }

      return false;
   }

   /**
    * Return the hash code of the nested object.
    *
    * @return  The hash code of the nested object.
    */
   public int hashCode() {
      return hashCode;
   }

   /////////////////////////////////////////////////////////////////////////
   //                            Factory Methods                          //
   /////////////////////////////////////////////////////////////////////////

   /**
    * Create a <tt>SoftObject</tt> for the given object.
    *
    * @param obj     Object to reference.
    * @return        <tt>SoftObject</tt> or <tt>null</tt> if object is null.
    */
   public static SoftObject create(final Object obj) {
      if (obj == null) return null;
      else return new SoftObject(obj);
   }

   /**
    * Create a <tt>SoftObject</tt> for the given object.
    *
    * @param obj     Object to reference.
    * @param queue   Reference queue.
    * @return        <tt>SoftObject</tt> or <tt>null</tt> if object is null.
    */
   public static SoftObject create(final Object obj,
                                   final ReferenceQueue queue)
   {
      if (obj == null) return null;
      else return new SoftObject(obj, queue);
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util.property;

import java.security.AccessController;
import java.security.PrivilegedAction;

/**
 * Provides shorter method names for working with the {@link PropertyManager}.
 *
 * @version <tt>$Revision$</tt>
 * @author  Jason Dillon
 * @author  Adrian Brock
 */
@SuppressWarnings("unchecked")
public final class Property
{
   /** Platform dependent line separator. */
   public static String LINE_SEPARATOR;

   /** Platform dependant file separator. */
   public static String FILE_SEPARATOR;

   /** Platform dependant path separator. */
   public static String PATH_SEPARATOR;

   static
   {
      PrivilegedAction action = new PrivilegedAction()
      {
         public Object run()
         {
            LINE_SEPARATOR = Property.get("line.separator");
            FILE_SEPARATOR = Property.get("file.separator");
            PATH_SEPARATOR = Property.get("path.separator");
            return null;
         }
      };
      AccessController.doPrivileged(action);
   }

   /**
    * Add a property listener
    *
    * @param listener   Property listener to add
    */
   public static void addListener(PropertyListener listener)
   {
      PropertyManager.addPropertyListener(listener);
   }

   /**
    * Add an array of property listeners
    *
    * @param listeners     Array of property listeners to add
    */
   public static void addListeners(PropertyListener[] listeners)
   {
      PropertyManager.addPropertyListeners(listeners);
   }

   /**
    * Remove a property listener
    *
    * @param listener   Property listener to remove
    * @return           True if listener was removed
    */
   public static boolean removeListener(PropertyListener listener)
   {
      return PropertyManager.removePropertyListener(listener);
   }

   /**
    * Set a property
    *
    * @param name    Property name
    * @param value   Property value
    * @return        Previous property value or null
    */
   public static String set(String name, String value)
   {
      return PropertyManager.setProperty(name, value);
   }

   /**
    * Remove a property
    *
    * @param name    Property name
    * @return        Removed property value or null
    */
   public static String remove(String name)
   {
      return PropertyManager.getProperty(name);
   }

   /**
    * Get a property
    *
    * @param name          Property name
    * @param defaultValue  Default property value
    * @return              Property value or default
    */
   public static String get(String name, String defaultValue)
   {
      return PropertyManager.getProperty(name, defaultValue);
   }

   /**
    * Get a property
    *
    * @param name       Property name
    * @return           Property value or null
    */
   public static String get(String name)
   {
      return PropertyManager.getProperty(name);
   }

   /**
    * Get an array style property
    *
    * @param base          Base property name
    * @param defaultValues Default property values
    * @return              Array of property values or default
    */
   public static String[] getArray(String base, String[] defaultValues)
   {
      return PropertyManager.getArrayProperty(base, defaultValues);
   }

   /**
    * Get an array style property
    *
    * @param name       Property name
    * @return           Array of property values or empty array
    */
   public static String[] getArray(String name)
   {
      return PropertyManager.getArrayProperty(name);
   }

   /**
    * Check if a property of the given name exists.
    *
    * @param name    Property name
    * @return        True if property exists
    */
   public static boolean exists(String name)
   {
      return PropertyManager.containsProperty(name);
   }

   /**
    * Get a property group for the given property base
    *
    * @param basename   Base property name
    * @return           Property group
    */
   public static PropertyGroup getGroup(String basename)
   {
      return PropertyManager.getPropertyGroup(basename);
   }

   /**
    * Get a property group for the given property base at the given index
    *
    * @param basename   Base property name
    * @param index      Array property index
    * @return           Property group
    */
   public static PropertyGroup getGroup(String basename, int index)
   {
      return PropertyManager.getPropertyGroup(basename, index);
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util.state;

import java.util.HashSet;
import java.util.Set;
import java.util.Iterator;
import org.jboss.logging.Logger;

/** The representation of a finite state machine.
 *
 * @author Scott.Stark@jboss.org
 * @version $Revision$
 */
@SuppressWarnings("unchecked")
public class StateMachine implements Cloneable
{
   private static Logger log = Logger.getLogger(StateMachine.class);
   /** A description of the state machine */
   private String description;
   /** The set of states making up the state machine */
   private HashSet states;
   /** The starting state */
   private State startState;
   /** The current state of the state machine */
   private State currentState;

   /** Create a state machine given its states and start state.
    *
    * @param states - Set<State> for the state machine
    * @param startState - the starting state
    */
   public StateMachine(Set states, State startState)
   {
      this(states, startState, null);
   }
   /** Create a state machine given its states and start state.
    *
    * @param states - Set<State> for the state machine
    * @param startState - the starting state
    * @param description - an optional description of the state machine
    */
   public StateMachine(Set states, State startState, String description)
   {
      this.states = new HashSet(states);
      this.startState = startState;
      this.currentState = startState;
      this.description = description;
   }

   /** Make a copy of the StateMachine maintaining the current state.
    *
    * @return a copy of the StateMachine.
    */
   public Object clone()
   {
      StateMachine clone = new StateMachine(states, startState, description);
      clone.currentState = currentState;
      return clone;
   }

   /** Get the state machine description.
    * @return an possibly null description.
    */
   public String getDescription()
   {
      return description;
   }

   /** Get the current state of the state machine.
    * @return the current state.
    */
   public State getCurrentState()
   {
      return currentState;
   }

   /** Get the start state of the state machine.
    * @return the start state.
    */
   public State getStartState()
   {
      return startState;
   }

   /** Get the states of the state machine.
    * @return the machine states.
    */
   public Set getStates()
   {
      return states;
   }

   /** Transition to the next state given the name of a valid transition.
    * @param actionName - the name of transition that is valid for the
    * current state.
    * @return the next state
    * @throws IllegalTransitionException
    */
   public State nextState(String actionName)
      throws IllegalTransitionException
   {
      Transition t = currentState.getTransition(actionName);
      if( t == null )
      {
         String msg = "No transition for action: '" + actionName
            + "' from state: '" + currentState.getName() + "'";
         throw new IllegalTransitionException(msg);
      }
      State nextState = t.getTarget();
      log.trace("nextState("+actionName+") = "+nextState);
      currentState = nextState;
      return currentState;
   }

   /** Reset the state machine back to the start state
    *
    * @return the start state
    */
   public State reset()
   {
      this.currentState = startState;
      return currentState;
   }

   public String toString()
   {
      StringBuffer tmp = new StringBuffer("StateMachine[:\n");
      tmp.append("\tCurrentState: "+currentState.getName());
      Iterator i = states.iterator();
      while( i.hasNext() )
      {
         tmp.append('\n').append(i.next());
      }
      tmp.append(']');
      return tmp.toString();
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util.collection;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

/**
 * An implementation of Set that manages a map of soft references to
 * the set values. The map is keyed by the value hashCode and so this
 * is only useful for value whose hashCode is a valid identity
 * representation (String, primative wrappers, etc).
 *
 * @author Scott.Stark@jboss.org
 * @version $Revision$
 */
@SuppressWarnings("unchecked")
public class SoftSet implements Set
{
   private HashMap map = new HashMap();
   /** The queue of garbage collected soft references */
   private ReferenceQueue gcqueue = new ReferenceQueue();

   static class ComparableSoftReference extends SoftReference
   {
      private Integer key;
      ComparableSoftReference(Integer key, Object o, ReferenceQueue q)
      {
         super(o, q);
         this.key = key;
      }
      Integer getKey()
      {
         return key;
      }
   }
   static class ComparableSoftReferenceIterator implements Iterator
   {
      Iterator theIter;
      ComparableSoftReferenceIterator(Iterator theIter)
      {
         this.theIter = theIter;
      }
      public boolean hasNext()
      {
         return theIter.hasNext();
      }
      public Object next()
      {
         ComparableSoftReference csr = (ComparableSoftReference) theIter.next();
         return csr.get();
      }
      public void remove()
      {
         theIter.remove();
      }
   }

   /**
    *
    */
   public SoftSet()
   {
   }

   public int size()
   {
      processQueue();
      return map.size();
   }

   public boolean isEmpty()
   {
      processQueue();
      return map.isEmpty();
   }

   public boolean contains(Object o)
   {
      processQueue();
      Integer key = new Integer(o.hashCode());
      boolean contains = map.containsKey(key);
      return contains;
   }

   public Iterator iterator()
   {
      processQueue();
      Iterator theIter = map.values().iterator();
      return new ComparableSoftReferenceIterator(theIter);
   }

   public Object[] toArray()
   {
      processQueue();
      return toArray(new Object[0]);
   }

   public Object[] toArray(Object[] a)
   {
      processQueue();
      int size = map.size();
      Object[] array = {};
      if( a.length >= size )
         array = a;
      Iterator iter = map.values().iterator();
      int index = 0;
      while( iter.hasNext() )
      {
         ComparableSoftReference csr = (ComparableSoftReference) iter.next();
         Object value = csr.get();
         // Create the correct array type
         if( array.length == 0 )
         {
            if( value == null )
            {
               index ++;
               continue;
            }
            Array.newInstance(value.getClass(), size);
         }
         array[index] = value;
         index ++;
      }
      return array;
   }

   public boolean add(Object o)
   {
      processQueue();
      Integer key = new Integer(o.hashCode());
      ComparableSoftReference sr = new ComparableSoftReference(key, o, gcqueue);
      return map.put(key, sr) == null;
   }

   public boolean remove(Object o)
   {
      processQueue();
      Integer key = new Integer(o.hashCode());
      return map.remove(key) != null;
   }

   public boolean containsAll(Collection c)
   {
      processQueue();
      Iterator iter = c.iterator();
      boolean contains = true;
      while( iter.hasNext() )
      {
         Object value = iter.next();
         Integer key = new Integer(value.hashCode());
         contains &= map.containsKey(key);
      }
      return contains;
   }

   public boolean addAll(Collection c)
   {
      processQueue();
      Iterator iter = c.iterator();
      boolean added = false;
      while( iter.hasNext() )
      {
         Object value = iter.next();
         Integer key = new Integer(value.hashCode());
         ComparableSoftReference sr = new ComparableSoftReference(key, value, gcqueue);
         added |= map.put(key, sr) == null;
      }
      return added;
   }

   public boolean retainAll(Collection c)
   {
      Iterator iter = iterator();
      boolean removed = false;
      while( iter.hasNext() )
      {
         Object value = iter.next();
         if( c.contains(value) == false )
         {
            iter.remove();
            removed = true;
         }
      }
      return removed;
   }

   public boolean removeAll(Collection c)
   {
      processQueue();
      Iterator iter = c.iterator();
      boolean removed = false;
      while( iter.hasNext() )
      {
         Object value = iter.next();
         removed |= remove(value);
      }
      return removed;
   }

   public void clear()
   {
      while( gcqueue.poll() != null )
         ;
      map.clear();
   }

   public boolean equals(Object o)
   {
      return map.equals(o);
   }

   public int hashCode()
   {
      return map.hashCode();
   }

   /**
    * Iterate through the gcqueue for for any cleared reference, remove
    * the associated value from the underlying set.
    */
   private void processQueue()
   {
      ComparableSoftReference cr;
      while( (cr = (ComparableSoftReference) gcqueue.poll()) != null )
      {
         map.remove(cr.getKey());
      }
   }

}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util.collection;

import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * A weak class cache that instantiates does not a hold a
 * strong reference to either the classloader or class.

 *
 * It creates the class specific data in two stages
 * to avoid recursion.

 *
 * instantiate - creates the data

 * generate - fills in the details
 *
 * @param <T> the cached type
 * @author Scott.Stark@jboss.org
 * @author Adrian Brock
 */
public abstract class WeakTypeCache<T>
{
   /** The cache */
   private Map<ClassLoader, Map<String, T>> cache = new WeakHashMap<ClassLoader, Map<String, T>>();

   /**
    * Get the information for a type
    *
    * @param type the type
    * @return the info
    */
   @SuppressWarnings({"unchecked", "cast"})
   public T get(Type type)
   {
      if (type == null)
         throw new IllegalArgumentException("Null type");

      if (type instanceof ParameterizedType)
         return getParameterizedType((ParameterizedType) type);
      else if (type instanceof Class)
         return getClass((Class<?>) type);
      else if (type instanceof TypeVariable)
         // TODO Figure out why we need this cast with the Sun compiler?
         return (T) getTypeVariable((TypeVariable) type);
      else if (type instanceof GenericArrayType)
         return getGenericArrayType((GenericArrayType) type);
      else if (type instanceof WildcardType)
         return getWildcardType((WildcardType) type);
      else
         throw new UnsupportedOperationException("Unknown type: " + type + " class=" + type.getClass());
   }

   /**
    * Get the information for a class
    *
    * @param name the name
    * @param cl the classloader
    * @return the info
    * @throws ClassNotFoundException when the class cannot be found
    */
   public T get(String name, ClassLoader cl) throws ClassNotFoundException
   {
      if (name == null)
         throw new IllegalArgumentException("Null name");
      if (cl == null)
         throw new IllegalArgumentException("Null classloader");

      Class<?> clazz = cl.loadClass(name);
      return get(clazz);
   }

   /**
    * Instantiate for a class
    *
    * @param clazz the class
    * @return the result
    */
   protected abstract T instantiate(Class<?> clazz);

   /**
    * Fill in the result
    *
    * @param clazz the class
    * @param result the result
    */
   protected abstract void generate(Class<?> clazz, T result);

   /**
    * Instantiate for a parameterized type
    *
    * @param type the parameterized type
    * @return the result
    */
   protected abstract T instantiate(ParameterizedType type);

   /**
    * Fill in the result
    *
    * @param type the parameterized type
    * @param result the result
    */
   protected abstract void generate(ParameterizedType type, T result);

   /**
    * Get the information for a parameterized type
    *
    * @param type the parameterized type
    * @return the info
    */
   protected T getParameterizedType(ParameterizedType type)
   {
      // First check if we already have it
      T result = peek(type);
      if (result != null)
         return result;

      // Instantiate
      result = instantiate(type);

      // Put the perlimanary result into the cache
      put(type, result);

      // Generate the details
      generate(type, result);

      return result;
   }

   /**
    * Get the information for a wildcard type
    *
    * @param type the paremeterized type
    * @return the info
    */
   protected T getWildcardType(WildcardType type)
   {
      // TODO JBMICROCONT-131 improve this
      return get(type.getUpperBounds()[0]);
   }

   /**
    * Get the information for a type variable
    *
    * @param <D> the declaration
    * @param type the type variable
    * @return the info
    */
   protected <D extends GenericDeclaration> T getTypeVariable(TypeVariable<D> type)
   {
      // TODO JBMICROCONT-131 improve this
      return get(type.getBounds()[0]);
   }

   /**
    * Get the information for an array type
    *
    * @param type the array type
    * @return the info
    */
   protected T getGenericArrayType(GenericArrayType type)
   {
      // TODO JBMICROCONT-131 this needs implementing properly
      return get(Object[].class);
   }

   /**
    * Peek into the cache
    *
    * @param type the type
    * @return the value
    */
   protected T peek(ParameterizedType type)
   {
      Class<?> rawType = (Class<?>) type.getRawType();
      ClassLoader cl = SecurityActions.getClassLoader(rawType);
      Map<String, T> classLoaderCache = getClassLoaderCache(cl);

      synchronized (classLoaderCache)
      {
         return classLoaderCache.get(type.toString());
      }
   }

   /**
    * Put a result into the cache
    *
    * @param type the type
    * @param result the value
    */
   protected void put(ParameterizedType type, T result)
   {
      Class<?> rawType = (Class<?>) type.getRawType();
      ClassLoader cl = SecurityActions.getClassLoader(rawType);
      Map<String, T> classLoaderCache = getClassLoaderCache(cl);

      synchronized (classLoaderCache)
      {
         // TODO JBMICROCONT-131 something better than toString()?
         classLoaderCache.put(type.toString(), result);
      }
   }

   /**
    * Get the information for a class
    *
    * @param clazz the class
    * @return the info
    */
   protected T getClass(Class<?> clazz)
   {
      // First check if we already have it
      T result = peek(clazz);
      if (result != null)
         return result;

      // Instantiate
      result = instantiate(clazz);

      // Put the preliminary result into the cache
      put(clazz, result);

      // Generate the details
      generate(clazz, result);

      return result;
   }

   /**
    * Peek into the cache
    *
    * @param clazz the class
    * @return the value
    */
   protected T peek(Class<?> clazz)
   {
      ClassLoader cl = SecurityActions.getClassLoader(clazz);
      Map<String, T> classLoaderCache = getClassLoaderCache(cl);

      synchronized (classLoaderCache)
      {
         return classLoaderCache.get(clazz.getName());
      }
   }

   /**
    * Put a result into the cache
    *
    * @param clazz the class
    * @param result the value
    */
   protected void put(Class<?> clazz, T result)
   {
      ClassLoader cl = SecurityActions.getClassLoader(clazz);
      Map<String, T> classLoaderCache = getClassLoaderCache(cl);

      synchronized (classLoaderCache)
      {
         classLoaderCache.put(clazz.getName(), result);
      }
   }

   /**
    * Get the cache for the classloader
    *
    * @param cl the classloader
    * @return the map
    */
   protected Map<String, T> getClassLoaderCache(ClassLoader cl)
   {
      synchronized (cache)
      {
         Map<String, T> result = cache.get(cl);
         if (result == null)
         {
            result = new WeakValueHashMap<String, T>();
            cache.put(cl, result);
         }
         return result;
      }
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util;

import java.io.PrintWriter;
import java.io.PrintStream;

/**
 * A common superclass for <tt>RuntimeException</tt> classes that can
 * contain a nested <tt>Throwable</tt> detail object.
 *
 * @version <tt>$Revision$</tt>
 * @author  Jason Dillon
 */
public class NestedRuntimeException
   extends RuntimeException
   implements NestedThrowable
{
   /** The serialVersionUID */
   private static final long serialVersionUID = 9017248167673450852L;
   /** The nested throwable */
   protected final Throwable nested;

   /**
    * Construct a <tt>NestedRuntimeException</tt> with the specified
    * detail message.
    *
    * @param msg  Detail message.
    */
   public NestedRuntimeException(final String msg) {
      super(msg);
      this.nested = null;
   }

   /**
    * Construct a <tt>NestedRuntimeException</tt> with the specified
    * detail message and nested <tt>Throwable</tt>.
    *
    * @param msg     Detail message.
    * @param nested  Nested <tt>Throwable</tt>.
    */
   public NestedRuntimeException(final String msg, final Throwable nested) {
      super(msg);
      this.nested = nested;
      NestedThrowable.Util.checkNested(this, nested);
   }

   /**
    * Construct a <tt>NestedRuntimeException</tt> with the specified
    * nested <tt>Throwable</tt>.
    *
    * @param nested  Nested <tt>Throwable</tt>.
    */
   public NestedRuntimeException(final Throwable nested) {
      this(nested.getMessage(), nested);
   }

   /**
    * Construct a <tt>NestedRuntimeException</tt> with no detail.
    */
   public NestedRuntimeException() {
      super();
      this.nested = null;
   }

   /**
    * Return the nested <tt>Throwable</tt>.
    *
    * @return  Nested <tt>Throwable</tt>.
    */
   public Throwable getNested() {
      return nested;
   }

   /**
    * Return the nested <tt>Throwable</tt>.
    *
    *
For JDK 1.4 compatibility.
    *
    * @return  Nested <tt>Throwable</tt>.
    */
   public Throwable getCause() {
      return nested;
   }

   /**
    * Returns the composite throwable message.
    *
    * @return  The composite throwable message.
    */
   public String getMessage() {
      return NestedThrowable.Util.getMessage(super.getMessage(), nested);
   }

   /**
    * Prints the composite message and the embedded stack trace to the
    * specified print stream.
    *
    * @param stream  Stream to print to.
    */
   public void printStackTrace(final PrintStream stream) {
      if (nested == null || NestedThrowable.PARENT_TRACE_ENABLED) {
         super.printStackTrace(stream);
      }
      NestedThrowable.Util.print(nested, stream);
   }

   /**
    * Prints the composite message and the embedded stack trace to the
    * specified print writer.
    *
    * @param writer  Writer to print to.
    */
   public void printStackTrace(final PrintWriter writer) {
      if (nested == null || NestedThrowable.PARENT_TRACE_ENABLED) {
         super.printStackTrace(writer);
      }
      NestedThrowable.Util.print(nested, writer);
   }

   /**
    * Prints the composite message and the embedded stack trace to
    * <tt>System.err</tt>.
    */
   public void printStackTrace() {
      printStackTrace(System.err);
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util.propertyeditor;

import java.util.ArrayList;
import java.util.StringTokenizer;
import java.beans.PropertyEditorSupport;

/** A property editor for Class[].
 *
 * @version $Revision$
 * @author Scott.Stark@jboss.org
 */
public class ClassArrayEditor extends PropertyEditorSupport
{
   /** Build a Class[] from a comma/whitespace seperated list of classes
    * @param text - the class name list
    */
   public void setAsText(final String text) throws IllegalArgumentException
   {
      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      StringTokenizer tokenizer = new StringTokenizer(text, ", \t\r\n");
      ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
      while( tokenizer.hasMoreTokens() == true )
      {
         String name = tokenizer.nextToken();
         try
         {
            Class<?> c = loader.loadClass(name);
            classes.add(c);
         }
         catch(ClassNotFoundException e)
         {
            throw new IllegalArgumentException("Failed to find class: "+name);
         }
      }

      Class<?>[] theValue = new Class[classes.size()];
      classes.toArray(theValue);
      setValue(theValue);
   }

   /**
    * @return a comma seperated string of the class array
    */
   public String getAsText()
   {
      Class<?>[] theValue = (Class[]) getValue();
      StringBuffer text = new StringBuffer();
      int length = theValue == null ? 0 : theValue.length;
      for(int n = 0; n < length; n ++)
      {
         text.append(theValue[n].getName());
         text.append(',');
      }
      // Remove the trailing ','
      text.setLength(text.length()-1);
      return text.toString();
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util.stream;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.rmi.Remote;
import java.rmi.server.RemoteObject;
import java.rmi.server.RemoteStub;

/**
 * An ObjectOutputStream subclass used by the MarshalledValue class to
 * ensure the classes and proxies are loaded using the thread context
 * class loader. Currently this does not do anything as neither class or
 * proxy annotations are used.
 *
 * @author Scott.Stark@jboss.org
 * @version $Revision: 37406 $
 */
public class MarshalledValueOutputStream
   extends ObjectOutputStream
{
   /** Creates a new instance of MarshalledValueOutputStream
    If there is a security manager installed, this method requires a
    SerializablePermission("enableSubstitution") permission to ensure it's
    ok to enable the stream to do replacement of objects in the stream.
    * @param os
    * @throws IOException
    */
   public MarshalledValueOutputStream(OutputStream os) throws IOException
   {
      super(os);
      enableReplaceObject(true);
   }

   /**
    * @throws java.io.IOException   Any exception thrown by the underlying OutputStream.
    */
   protected void annotateClass(Class<?> cl) throws IOException
   {
      super.annotateClass(cl);
   }

   /**
    * @throws java.io.IOException   Any exception thrown by the underlying OutputStream.
    */
   protected void annotateProxyClass(Class<?> cl) throws IOException
   {
      super.annotateProxyClass(cl);
   }

   /** Override replaceObject to check for Remote objects that are
    not RemoteStubs.
   */
   protected Object replaceObject(Object obj) throws IOException
   {
      if( (obj instanceof Remote) && !(obj instanceof RemoteStub) )
      {
         Remote remote = (Remote) obj;
         try
         {
            obj = RemoteObject.toStub(remote);
         }
         catch(IOException ignore)
         {
            // Let the Serialization layer try with the orignal obj
         }
      }
      return obj;
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util.threadpool;

import org.jboss.logging.Logger;

/**
 * A wrapper for the task.
 *
 * @author Adrian Brock
 * @version $Revision$
 */
public class BasicTaskWrapper implements TaskWrapper
{
   /** The log */
   private static final Logger log = Logger.getLogger(BasicTaskWrapper.class);

   /** The task has not been accepted */
   public static final int TASK_NOT_ACCEPTED = 0;

   /** The task has been accepted */
   public static final int TASK_ACCEPTED = 1;

   /** The task has been started */
   public static final int TASK_STARTED = 2;

   /** The task has completed */
   public static final int TASK_COMPLETED = 3;

   /** The task was rejected */
   public static final int TASK_REJECTED = -1;

   /** The task has been stopped */
   public static final int TASK_STOPPED = -2;

   /** The state of the task */
   private int state = TASK_NOT_ACCEPTED;

   /** The state lock */
   private Object stateLock = new Object();

   /** The task */
   private Task task;

   /** The task as a string */
   private String taskString;

   /** The start time */
   private long startTime;

   /** The start timeout */
   private long startTimeout;

   /** The completion timeout */
   private long completionTimeout;

   /** The priority */
   private int priority;

   /** The wait type */
   private int waitType;

   /** The thread */
   private Thread runThread;

   /**
    * Create a task wrapper without a task
    */
   protected BasicTaskWrapper()
   {
   }

   /**
    * Create a new task wrapper
    *
    * @param task the task
    * @throws IllegalArgumentException for a null task
    */
   public BasicTaskWrapper(Task task)
   {
      setTask(task);
   }

   public int getTaskWaitType()
   {
      return waitType;
   }

   public int getTaskPriority()
   {
      return priority;
   }

   public long getTaskStartTimeout()
   {
      return startTimeout;
   }

   public long getTaskCompletionTimeout()
   {
      return completionTimeout;
   }

   public void acceptTask()
   {
      synchronized (stateLock)
      {
         // Not in a valid state
         if (state != TASK_NOT_ACCEPTED)
            return;
      }

      // Accept the task
      if (taskAccepted())
         state = TASK_ACCEPTED;
      else
         state = TASK_REJECTED;

      // Notify the waiting task
      synchronized (stateLock)
      {
         stateLock.notifyAll();
      }
   }

   public void rejectTask(RuntimeException e)
   {
      synchronized (stateLock)
      {
         state = TASK_REJECTED;
         stateLock.notifyAll();
      }
      taskRejected(e);
   }

   public boolean isComplete()
   {
      return state == TASK_COMPLETED;
   }

   public void stopTask()
   {
      boolean started;
      synchronized (stateLock)
      {
         started = (state == TASK_STARTED);
         state = TASK_STOPPED;
      }
      if (started)
      {
         // Interrupt the run thread if its not null
         if( runThread != null )
         {
            runThread.interrupt();
         }
         taskStop();
      }
      else if( runThread != null && runThread.isInterrupted() )
      {
         /* If the thread has not been returned after being interrupted, then
         use the deprecated stop method to try to force the thread abort.
         */
         runThread.stop();
      }
   }

   public void waitForTask()
   {
      switch (waitType)
      {
         case Task.WAIT_FOR_START:
         {
            boolean interrupted = false;
            synchronized (stateLock)
            {
               while (state == TASK_NOT_ACCEPTED || state == TASK_ACCEPTED)
               {
                  try
                  {
                     stateLock.wait();
                  }
                  catch (InterruptedException e)
                  {
                     interrupted = true;
                  }
               }
               if (interrupted)
                  Thread.currentThread().interrupt();
               return;
            }
         }
         default:
         {
            return;
         }
      }
   }

   /**
    * Called by the thread pool executor
    */
   public void run()
   {
      // Get the execution thread
      this.runThread = Thread.currentThread();

      // Check for a start timeout
      long runTime = getElapsedTime();
      if (startTimeout > 0l && runTime >= startTimeout)
      {
         taskRejected(new StartTimeoutException("Start Timeout exceeded for task " + taskString));
         return;
      }

      // We are about to start, check for a stop
      boolean stopped = false;
      synchronized (stateLock)
      {
         if (state == TASK_STOPPED)
         {
            stopped = true;
         }
         else
         {
            state = TASK_STARTED;
            taskStarted();
            if (waitType == Task.WAIT_FOR_START)
               stateLock.notifyAll();
         }
      }
      if (stopped)
      {
         taskRejected(new TaskStoppedException("Task stopped for task " + taskString));
         return;
      }

      // Run the task
      Throwable throwable = null;
      try
      {
         task.execute();
      }
      catch (Throwable t)
      {
         throwable = t;
      }

      // It is complete
      taskCompleted(throwable);

      // We are completed
      synchronized (stateLock)
      {
         state = TASK_COMPLETED;
         if (waitType == Task.WAIT_FOR_COMPLETE)
            stateLock.notifyAll();
      }
   }

   /**
    * Set thetask for this wrapper
    *
    * @param task the task
    */
   protected void setTask(Task task)
   {
      if (task == null)
         throw new IllegalArgumentException("Null task");
      this.task = task;
      this.taskString = task.toString();
      this.startTime = System.currentTimeMillis();
      this.waitType = task.getWaitType();
      this.priority = task.getPriority();
      this.startTimeout = task.getStartTimeout();
      this.completionTimeout = task.getCompletionTimeout();
   }

   /**
    * Notify the task it has been accepted
    *
    * @return true when the notification succeeds, false otherwise
    */
   protected boolean taskAccepted()
   {
      try
      {
         task.accepted(getElapsedTime());
         return true;
      }
      catch (Throwable t)
      {
         log.warn("Unexpected error during 'accepted' for task: " + taskString, t);
         return false;
      }
   }

   /**
    * Notify the task it has been rejected
    *
    * @param e any error associated with the rejection
    * @return true when the notification succeeds, false otherwise
    */
   protected boolean taskRejected(RuntimeException e)
   {
      try
      {
         task.rejected(getElapsedTime(), e);
         return true;
      }
      catch (Throwable t)
      {
         log.warn("Unexpected error during 'rejected' for task: " + taskString, t);
         if (e != null)
            log.warn("Original reason for rejection of task: " + taskString, e);
         return false;
      }
   }

   /**
    * Notify the task it has started
    *
    * @return true when the notification succeeds, false otherwise
    */
   protected boolean taskStarted()
   {
      try
      {
         task.started(getElapsedTime());
         return true;
      }
      catch (Throwable t)
      {
         log.warn("Unexpected error during 'started' for task: " + taskString, t);
         return false;
      }
   }

   /**
    * Notify the task it has completed
    *
    * @param throwable any throwable associated with the completion
    * @return true when the notification succeeds, false otherwise
    */
   protected boolean taskCompleted(Throwable throwable)
   {
      try
      {
         task.completed(getElapsedTime(), throwable);
         return true;
      }
      catch (Throwable t)
      {
         log.warn("Unexpected error during 'completed' for task: " + taskString, t);
         if (throwable != null)
            log.warn("Original error during 'run' for task: " + taskString, throwable);
         return false;
      }
   }

   /**
    * Stop the task
    *
    * @return true when the notification succeeds, false otherwise
    */
   protected boolean taskStop()
   {
      try
      {
         task.stop();
         return true;
      }
      catch (Throwable t)
      {
         log.warn("Unexpected error during 'stop' for task: " + taskString, t);
         return false;
      }
   }

   /**
    * Calculate the elapsed time since the task was started
    *
    * @return the elapsed time in millis
    */
   protected long getElapsedTime()
   {
      return System.currentTimeMillis() - startTime;
   }

   /**
    * Get the state as a string
    *
    * @return the state string
    */
   protected String getStateString()
   {
      switch (state)
      {
         case TASK_NOT_ACCEPTED:
            return "NOT_ACCEPTED";
         case TASK_REJECTED:
            return "REJECTED";
         case TASK_ACCEPTED:
            return "ACCEPTED";
         case TASK_STARTED:
            return "STARTED";
         case TASK_STOPPED:
            return "STOPPED";
         case TASK_COMPLETED:
            return "COMPLETED";
         default:
            return "???";
      }
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util;

/**
 * Class that queues {@link Executable} jobs that are executed sequentially
 * by a single thread.
 *
 * @see Executable
 *
 * @author Simone Bordet
 * @version $Revision$
 */
public class WorkerQueue
{
   /** The thread that runs the Executable jobs */
   protected Thread m_queueThread;

   /** The job that will be executed by the worker thread */
   private JobItem m_currentJob;

   /**
    * Creates a new worker queue with default thread name of "Worker Thread"
    */
   public WorkerQueue()
   {
      this("Worker Thread");
   }

   /**
    * Creates a new worker queue with the specified thread name
    * @param threadName
    */
   public WorkerQueue(String threadName)
   {
      m_queueThread = new Thread(createQueueLoop(), threadName);
   }

   /**
    * Creates a new worker queue with the specified thread name
    * and daemon mode flag
    * @param threadName
    * @param isDaemon
    */
   public WorkerQueue(String threadName, boolean isDaemon)
   {
      m_queueThread = new Thread(createQueueLoop(), threadName);
      m_queueThread.setDaemon(isDaemon);
   }

   /**
    * Starts the worker queue.
    * @see #stop
    */
   public void start()
   {
      if (m_queueThread != null) {m_queueThread.start();}
   }

   /**
    * Stops nicely the worker queue.

    * After this call trying to put a new job will result in a
    * InterruptedException to be thrown. The jobs queued before and not
    * yet processed are processed until the queue is empty, then this
    * worker queue is cleared.
    * @see #clear
    * @see #start
    * @see #isInterrupted
    */
   public synchronized void stop()
   {
      if (m_queueThread != null) {m_queueThread.interrupt();}
   }

   /**
    * Called by a thread that is not the WorkerQueue thread, this method
    * queues the job and, if necessary, wakes up this worker queue that is
    * waiting in {@link #getJob}.
    * @param job
    */
   public synchronized void putJob(Executable job)
   {
      // Preconditions
      if (m_queueThread == null || !m_queueThread.isAlive()) {
         throw new IllegalStateException("Can't put job, thread is not alive or not present");
      }

      if (isInterrupted()) {
         throw new IllegalStateException("Can't put job, thread was interrupted");
      }

      putJobImpl(job);
   }

   /**
    * @return whether the worker thread has been interrupted.

    * When this method returns true, it is not possible to put new jobs in the
    * queue and the already present jobs are executed and removed from the
    * queue, then the thread exits.
    *
    * @see #stop
    */
   protected boolean isInterrupted()
   {
      return m_queueThread.isInterrupted();
   }

   /**
    * Called by this class, this method checks if the queue is empty;
    * if it is, then waits, else returns the current job.
    *
    * @see #putJob
    * @return the executable
    * @throws InterruptedException
    */
   protected synchronized Executable getJob() throws InterruptedException
   {
      // Preconditions
      if (m_queueThread == null || !m_queueThread.isAlive()) {
         throw new IllegalStateException();
      }

      return getJobImpl();
   }

   /**
    * Never call this method, only override in subclasses to perform
    * job getting in a specific way, normally tied to the data structure
    * holding the jobs.
    * @return the exectuable
    * @throws InterruptedException
    */
   protected Executable getJobImpl() throws InterruptedException
   {
      // While the queue is empty, wait();
      // when notified take an event from the queue and return it.
      while (m_currentJob == null) {wait();}
      // This one is the job to return
      JobItem item = m_currentJob;
      // Go on to the next object for the next call.
      m_currentJob = m_currentJob.m_next;
      return item.m_job;
   }

   /**
    * Never call this method, only override in subclasses to perform
    * job adding in a specific way, normally tied to the data structure
    * holding the jobs.
    * @param job
    */
   protected void putJobImpl(Executable job)
   {
      JobItem posted = new JobItem(job);
      if (m_currentJob == null)
      {
         // The queue is empty, set the current job to process and
         // wake up the thread waiting in method getJob
         m_currentJob = posted;
         notifyAll();
      }
      else
      {
         JobItem item = m_currentJob;
         // The queue is not empty, find the end of the queue ad add the
         // posted job at the end
         while (item.m_next != null) {item = item.m_next;}
         item.m_next = posted;
      }
   }

   /**
    * Clears the running thread after the queue has been stopped.

    * After this call, this worker queue is unusable and can be garbaged.
    */
   protected void clear()
   {
      m_queueThread = null;
      m_currentJob = null;
   }

   /**
    * Creates the loop that will get the next job and process it.

    * Override in subclasses to create a custom loop.
    * @return the runnable
    */
   protected Runnable createQueueLoop() {
      return new QueueLoop();
   }

   /**
    * Class that loops getting the next job to be executed and then
    * executing it, in the worker thread.
    */
   protected class QueueLoop
      implements Runnable
   {
      public void run()
      {
         try
         {
            while (true)
            {
               try
               {
                  if (isInterrupted())
                  {
                     flush();
                     break;
                  }
                  else
                  {
                     getJob().execute();
                  }
               }
               catch (InterruptedException e)
               {
                  try {
                     flush();
                  }
                  catch (Exception ignored) {}
                  break;
               }
               catch (Exception e) {
                  ThrowableHandler.add(ThrowableHandler.Type.ERROR, e);
               }
            }
         }
         finally {
            clear();
         }
      }

      protected void flush() throws Exception
      {
         // Empty the queue of the posted jobs and exit
         while (m_currentJob != null)
         {
            m_currentJob.m_job.execute();
            m_currentJob = m_currentJob.m_next;
         }
      }
   }

   /**
    * Simple linked cell, that has only a reference to the next job.
    */
   private class JobItem
   {
      private Executable m_job;
      private JobItem m_next;
      private JobItem(Executable job) {m_job = job;}
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util.threadpool;

import java.io.Serializable;
import java.io.ObjectStreamException;

/** A type-safe enum for the BasicThreadPool blocking mode.
 *
 * @author Scott.Stark@jboss.org
 * @version $Revision$
 */
public class BlockingMode implements Serializable
{
   /** @since 1.0 */
   private static final long serialVersionUID = -9102277941374138830L;

   public static final int RUN_TYPE = 0;
   public static final int WAIT_TYPE = 1;
   public static final int DISCARD_TYPE = 2;
   public static final int DISCARD_OLDEST_TYPE = 3;
   public static final int ABORT_TYPE = 4;

   /** Set the policy for blocked execution to be that the current thread
      executes the command if there are no available threads in the pool.
    */
   public static final BlockingMode RUN = new BlockingMode("run", RUN_TYPE);
   /** Set the policy for blocked execution to be to wait until a thread
    * is available, unless the pool has been shut down, in which case
    * the action is discarded.
    */
   public static final BlockingMode WAIT = new BlockingMode("wait", WAIT_TYPE);
   /** Set the policy for blocked execution to be to return without
    * executing the request.
    */
   public static final BlockingMode DISCARD = new BlockingMode("discard", DISCARD_TYPE);
   /** Set the policy for blocked execution to be to discard the oldest
    * unhandled request
    */
   public static final BlockingMode DISCARD_OLDEST =
      new BlockingMode("discardOldest", DISCARD_OLDEST_TYPE);
   /** Set the policy for blocked execution to be to throw an AbortWhenBlocked
    * (a subclass of RuntimeException).
    */
   public static final BlockingMode ABORT = new BlockingMode("abort", ABORT_TYPE);

   /** The string form of the enum */
   private final transient String name;
   /** The enum manifest constant */
   private final int type;

   /** A utility method to convert a string name to a BlockingMode
    * @param name
    * @return The associated BlockingMode constant if name is valid, null otherwise
    */
   public static final BlockingMode toBlockingMode(String name)
   {
      BlockingMode mode = null;
      if( name == null )
      {
         mode = null;
      }
      else if( name.equalsIgnoreCase("run") )
      {
         mode = RUN;
      }
      else if( name.equalsIgnoreCase("wait") )
      {
         mode = WAIT;
      }
      else if( name.equalsIgnoreCase("discard") )
      {
         mode = DISCARD;
      }
      else if( name.equalsIgnoreCase("discardOldest") )
      {
         mode = DISCARD_OLDEST;
      }
      else if( name.equalsIgnoreCase("abort") )
      {
         mode = ABORT;
      }
      return mode;
   }

   private BlockingMode(String name, int type)
   {
      this.name = name;
      this.type = type;
   }

   public String toString()
   {
      return name;
   }

   /**
    * Overriden to return the indentity instance of BlockingMode based on the
    * stream type int value. This ensures that BlockingMode enums can be
    * compared using ==.
    *
    * @return The BlockingMode instance for the XXX_TYPE int.
    * @throws ObjectStreamException
    */
   Object readResolve() throws ObjectStreamException
   {
      // Replace the marshalled instance type with the local instance
      BlockingMode mode = ABORT;
      switch( type )
      {
         case RUN_TYPE:
            mode = RUN;
            break;
         case WAIT_TYPE:
            mode = RUN;
            break;
         case DISCARD_TYPE:
            mode = RUN;
            break;
         case DISCARD_OLDEST_TYPE:
            mode = RUN;
            break;
         case ABORT_TYPE:
            mode = RUN;
            break;
      }
      return mode;
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.util;

import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;

/**
 * Convenience class to wrap an <tt>Object</tt> into a <tt>WeakReference</tt>.
 *
 *
Modified from <tt>java.util.WeakHashMap.WeakKey</tt>.
 *
 * @version <tt>$Revision$</tt>
 * @author  Jason Dillon
 */
@SuppressWarnings("unchecked")
public final class WeakObject
   extends WeakReference
{
   /** The hash code of the nested object */
   protected final int hashCode;

   /**
    * Construct a <tt>WeakObject</tt>.
    *
    * @param obj  Object to reference.
    */
   public WeakObject(final Object obj) {
      super(obj);
      hashCode = obj.hashCode();
   }

   /**
    * Construct a <tt>WeakObject</tt>.
    *
    * @param obj     Object to reference.
    * @param queue   Reference queue.
    */
   public WeakObject(final Object obj, final ReferenceQueue queue) {
      super(obj, queue);
      hashCode = obj.hashCode();
   }

   /**
    * Check the equality of an object with this.
    *
    * @param obj  Object to test equality with.
    * @return     True if object is equal.
    */
   public boolean equals(final Object obj) {
      if (obj == this) return true;

      if (obj != null && obj.getClass() == getClass()) {
         WeakObject soft = (WeakObject)obj;

         Object a = this.get();
         Object b = soft.get();
         if (a == null || b == null) return false;
         if (a == b) return true;

         return a.equals(b);
      }

      return false;
   }

   /**
    * Return the hash code of the nested object.
    *
    * @return  The hash code of the nested object.
    */
   public int hashCode() {
      return hashCode;
   }

   /////////////////////////////////////////////////////////////////////////
   //                            Factory Methods                          //
   /////////////////////////////////////////////////////////////////////////

   /**
    * Create a <tt>WeakObject</tt> for the given object.
    *
    * @param obj     Object to reference.
    * @return        <tt>WeakObject</tt> or <tt>null</tt> if object is null.
    */
   public static WeakObject create(final Object obj) {
      if (obj == null) return null;
      else return new WeakObject(obj);
   }

   /**
    * Create a <tt>WeakObject</tt> for the given object.
    *
    * @param obj     Object to reference.
    * @param queue   Reference queue.
    * @return        <tt>WeakObject</tt> or <tt>null</tt> if object is null.
    */
   public static WeakObject create(final Object obj,
                                   final ReferenceQueue queue)
   {
      if (obj == null) return null;
      else return new WeakObject(obj, queue);
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.net.protocol.file;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;

import org.jboss.logging.Logger;
import org.jboss.net.protocol.URLListerBase;

/**
 * FileURLLister
 *
 * @author jboynes@users.sf.net
 * @author Dimitris Andreadis
 * @version $Revision$
 */
@SuppressWarnings("unchecked")
public class FileURLLister extends URLListerBase
{
   /** The Logger */
   private static final Logger log = Logger.getLogger(FileURLLister.class);

   // Public --------------------------------------------------------

   public Collection listMembers(URL baseUrl, URLFilter filter) throws IOException
   {
      return listMembers(baseUrl, filter, false);
   }

   public Collection<URL> listMembers(URL baseUrl, URLFilter filter, boolean scanNonDottedSubDirs) throws IOException
   {
      // Make sure this is a directory URL
      String baseUrlString = baseUrl.toString();
      if (!baseUrlString.endsWith("/"))
      {
         throw new IOException("Does not end with '/', not a directory url: " + baseUrlString);
      }

      // Verify the directory actually exists
      File dir = new File(baseUrl.getPath());
      if (!dir.isDirectory())
      {
         throw new FileNotFoundException("Not pointing to a directory, url: " + baseUrlString);
      }

      // The list of URLs to return
      ArrayList<URL> resultList = new ArrayList<URL>();

      // Do the actual job
      listFiles(baseUrl, filter, scanNonDottedSubDirs, resultList);

      // Done
      return resultList;
   }

   // Private -------------------------------------------------------

   /**
    * Starting from baseUrl, that should point to a directory, populate the
    * resultList with the contents that pass the filter (in the form of URLs)
    * and possibly recurse into subdris not containing a '.' in their name.
    */
   private void listFiles(final URL baseUrl, final URLFilter filter, boolean scanNonDottedSubDirs, ArrayList<URL> resultList)
      throws IOException
   {
      // List the files at the current dir level, using the provided filter
      final File baseDir = new File(baseUrl.getPath());
      String[] filenames = baseDir.list(new FilenameFilter()
      {
         public boolean accept(File dir, String name)
         {
            try
            {
               return filter.accept(baseUrl, name);
            }
            catch (Exception e)
            {
               log.debug("Unexpected exception filtering entry '" + name + "' in directory '" + baseDir + "'", e);
               return true;
            }
         }
      });

      if (filenames == null)
      {
         // This happens only when baseDir not a directory (but this is already
         // checked by the caller) or some unknown IOException happens internally
         // (e.g. run out of file descriptors?). Unfortunately the File API
         // doesn't provide a way to know.
         throw new IOException("Could not list directory '" + baseDir + "', reason unknown");
      }
      else
      {
         String baseUrlString = baseUrl.toString();

         for (int i = 0; i < filenames.length; i++)
         {
            String filename = filenames[i];

            // Find out if this is a directory
            File file = new File(baseDir, filename);
            boolean isDir = file.isDirectory();

            // The subUrl
            URL subUrl = createURL(baseUrlString, filename, isDir);

            // If scanning subdirs and we have a directory, not containing a '.' in
            // the name, recurse into it. This is to allow recursing into grouping
            // dirs like ./deploy/jms, ./deploy/management, etc., avoiding
            // at the same time exploded packages, like .sar, .war, etc.
            if (scanNonDottedSubDirs && isDir && (filename.indexOf('.') == -1))
            {
               // recurse into it
               listFiles(subUrl, filter, scanNonDottedSubDirs, resultList);
            }
            else
            {
               // just add to the list
               resultList.add(subUrl);
            }
         }
      }
   }

   /**
    * Create a URL by concatenating the baseUrlString that should end at '/',
    * the filename, and a trailing slash, if it points to a directory
    */
   private URL createURL(String baseUrlString, String filename, boolean isDirectory)
   {
      try
      {
         return new URL(baseUrlString + filename + (isDirectory ? "/" : ""));
      }
      catch (MalformedURLException e)
      {
         // shouldn't happen
         throw new IllegalStateException();
      }
   }

}