hlship      2004/04/18 10:39:58

  Modified:    .        .classpath
               framework/src/java/org/apache/hivemind/impl
                        RegistryBuilder.java ThreadedServiceModel.java
                        ServiceInterceptorContributionImpl.java
                        ConstructableServiceExtensionPoint.java
                        AbstractServiceModelImpl.java
                        ServiceExtensionPointImpl.java
               framework/src/java/org/apache/hivemind
                        HiveMindMessages.properties
                        ServiceInterceptorContribution.java HiveMind.java
               framework project.xml
               framework/src/test/hivemind/test/services TestShutdown.java
                        TestPooledServiceModel.java TestThreadedModel.java
                        AddSimpleInterceptors1.xml
                        AddSimpleInterceptors2.xml TestServices.java
               framework/src/java/org/apache/hivemind/parse
                        InterceptorDescriptor.java
                        DescriptorParser.properties DescriptorParser.java
               framework/src/test/hivemind/test TestMisc.java
               framework/src/test/hivemind/test/services/impl
                        TrackerFactory.java
               framework/src/test/hivemind/test/parse
                        TestDescriptorParser.java GenericModule.xml
                        BadElement.xml
               xdocs/images InterceptorStack.png
               xdocs    services.xml descriptor.xml
               src/images InterceptorStack.psp
               src/xsl  hivemind.xsl
  Added:       framework/src/java/org/apache/hivemind/order package.html
                        ObjectOrdering.java Orderer.java
               framework/src/test/hivemind/test/order TestOrderer.java
  Log:
  Add a dependency-based approach to ordering interceptors.
  
  Revision  Changes    Path
  1.24      +1 -0      jakarta-hivemind/.classpath
  
  Index: .classpath
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/.classpath,v
  retrieving revision 1.23
  retrieving revision 1.24
  diff -u -r1.23 -r1.24
  --- .classpath        7 Apr 2004 15:18:12 -0000       1.23
  +++ .classpath        18 Apr 2004 17:39:57 -0000      1.24
  @@ -20,5 +20,6 @@
       <classpathentry kind="var" path="MAVEN_REPO/oro/jars/oro-2.0.6.jar"/>
       <classpathentry kind="var" 
path="MAVEN_REPO/jboss/jars/javassist-2.6.jar"/>
       <classpathentry kind="var" 
path="MAVEN_REPO/jboss/jars/jboss-jmx-3.0.6.jar"/>
  +    <classpathentry kind="var" 
path="MAVEN_REPO/werkz/jars/werkz-1.0-beta-10.jar"/>
       <classpathentry kind="output" path="bin"/>
   </classpath>
  
  
  
  1.1                  
jakarta-hivemind/framework/src/java/org/apache/hivemind/order/package.html
  
  Index: package.html
  ===================================================================
  <!-- $Id: package.html,v 1.1 2004/04/18 17:39:57 hlship Exp $ -->
  <!-- 
     Copyright 2004 The Apache Software Foundation
  
     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.
  -->
        
  <body>
  
  Classes and interfaces for ordering objects based on dependencies. The 
underlying
  approach is provided by <a href="http://werkz.codehaus.org/";>Werkz</a>.
  
        </body>
  
  
  1.1                  
jakarta-hivemind/framework/src/java/org/apache/hivemind/order/ObjectOrdering.java
  
  Index: ObjectOrdering.java
  ===================================================================
  //  Copyright 2004 The Apache Software Foundation
  //
  // 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.apache.hivemind.order;
  
  /**
   * Used by an [EMAIL PROTECTED] org.apache.hivemind.order.Orderer} to organize
   * a single object and pre- and post-requisites.
   * 
   * @author Howard Lewis Ship
   * @version $Id: ObjectOrdering.java,v 1.1 2004/04/18 17:39:57 hlship Exp $
   */
  class ObjectOrdering
  {
      private String _name;
      private Object _object;
      private String _prereqs;
      private String _postreqs;
  
      ObjectOrdering(Object object, String name, String prereqs, String 
postreqs)
      {
  
          _object = object;
                _name = name;
          _prereqs = prereqs;
          _postreqs = postreqs;
      }
  
      public String getName()
      {
          return _name;
  
      }
      public Object getObject()
      {
          return _object;
      }
  
      public String getPostreqs()
      {
          return _postreqs;
      }
  
      public String getPrereqs()
      {
          return _prereqs;
      }
  
  }
  
  
  
  1.1                  
jakarta-hivemind/framework/src/java/org/apache/hivemind/order/Orderer.java
  
  Index: Orderer.java
  ===================================================================
  //  Copyright 2004 The Apache Software Foundation
  //
  // 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.apache.hivemind.order;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  
  import org.apache.commons.lang.StringUtils;
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.apache.hivemind.HiveMind;
  
  import com.werken.werkz.Action;
  import com.werken.werkz.CyclicGoalChainException;
  import com.werken.werkz.DefaultAction;
  import com.werken.werkz.Goal;
  import com.werken.werkz.Session;
  import com.werken.werkz.WerkzProject;
  
  /**
   * Used to order objects into an "execution" order.  Each object must have a 
name.
   * It may specify a list of pre-requisites and a list of post-requisites.
   *
   * @author Howard Lewis Ship
   * @version $Id: Orderer.java,v 1.1 2004/04/18 17:39:57 hlship Exp $
   */
  public class Orderer
  {
      private final Log _log;
      private final String _objectType;
      private List _orderingsList = null;
      private Map _orderingsMap = null;
      private List _orderedObjects;
      private Goal _leader;
      private Goal _trailer;
  
      /**
       * Creates an instance using 
<code>org.apache.hivemind.order.Orderer</code> as the Log.
       */
      public Orderer(String objectType)
      {
          this(LogFactory.getLog(Orderer.class), objectType);
      }
  
      /**
       * Creates a new instance, but directs all debug and error logging output
       * to the provided log.
       * 
       * @param log Used for logging any errors
       * @param objectType user presentable name for the type of object to be 
ordered; used in some error messages
       */
      public Orderer(Log log, String objectType)
      {
          _log = log;
          _objectType = objectType;
  
          HiveMind.checkNullParameter("log", log);
          HiveMind.checkNullParameter("log", objectType);
      }
  
      /**
       * Adds a new object.  All invocations of [EMAIL PROTECTED] #add(Object, 
String, String, String)}
       * should occur before invoking [EMAIL PROTECTED] #getOrderedObjects()}.
       * 
       * @param object an object to be sorted into order based on prereqs and 
postreqs
       * @param name a unique name for the 
       * @param prereqs a comma-separated list of the names of objects that 
should precede this
       * object in the list (or null)
       * @param postreqs a comma-separated list of the names of objects that 
should follow this
       * object in the list (or null)
       */
      public void add(Object object, String name, String prereqs, String 
postreqs)
      {
          if (_orderingsMap == null)
          {
              _orderingsMap = new HashMap();
              _orderingsList = new ArrayList();
          }
  
          ObjectOrdering o = getOrderable(name);
  
          if (o != null)
          {
              _log.error(
                  HiveMind.format(
                      "Orderer.duplicate-name",
                      new Object[] {
                          StringUtils.capitalize(_objectType),
                          name,
                          HiveMind.getLocationString(object),
                          HiveMind.getLocationString(o.getObject())}));
              return;
          }
  
          o = new ObjectOrdering(object, name, prereqs, postreqs);
  
          _orderingsMap.put(name, o);
          _orderingsList.add(o);
      }
  
      private ObjectOrdering getOrderable(String name)
      {
          return (ObjectOrdering) _orderingsMap.get(name);
      }
  
      /**
       * Uses the information provided by [EMAIL PROTECTED] #add(Object, 
String, String, String)} to order
       * the objects into an appropriate order based on the pre- and post-reqts 
provided.
       * Errors such as cyclic dependencies or unrecognized names are logged 
and ignored.
       */
      public List getOrderedObjects()
      {
          if (_orderingsMap == null)
              return Collections.EMPTY_LIST;
  
          try
          {
              _orderedObjects = new ArrayList(_orderingsMap.size());
  
              runGoals();
  
              return _orderedObjects;
          }
          finally
          {
              _orderedObjects = null;
              _leader = null;
              _trailer = null;
          }
      }
  
      private static class NullAction extends DefaultAction
      {
  
          public void performAction() throws Exception
          {
  
          }
  
          public void performAction(Session arg0) throws Exception
          {
  
          }
  
      }
  
      private void runGoals()
      {
          WerkzProject p = new WerkzProject();
  
          addGoals(p);
  
          if (_leader == null)
          {
              _leader = new Goal("*-leader-*", new NullAction());
              p.addGoal(_leader);
          }
  
          if (_trailer == null)
          {
              _trailer = new Goal("*-trailer-*", new NullAction());
  
              p.addGoal(_trailer);
          }
  
          addDependencies(p);
  
          try
          {
              _trailer.attain(new Session());
          }
          catch (Exception ex)
          {
              _log.error(HiveMind.format("Orderer.exception", _objectType, 
ex.getMessage()), ex);
          }
      }
  
      private void addGoals(WerkzProject project)
      {
          Iterator i = _orderingsList.iterator();
  
          while (i.hasNext())
          {
              final ObjectOrdering o = (ObjectOrdering) i.next();
  
              Action a = new DefaultAction()
              {
                  public void performAction()
                  {
                      _orderedObjects.add(o.getObject());
                  }
  
                  public void performAction(Session session)
                  {
                      performAction();
                  }
              };
  
              Goal goal = new Goal(o.getName(), a);
  
              project.addGoal(goal);
  
              if ("*".equals(o.getPostreqs()))
              {
                  if (_leader == null)
                      _leader = goal;
                  else
                  {
                      String leaderName = _leader.getName();
                      _log.error(
                          HiveMind.format(
                              "Orderer.dupe-leader",
                              new Object[] {
                                  StringUtils.capitalize(_objectType),
                                  o.getName(),
                                  HiveMind.getLocationString(o.getObject()),
                                  leaderName,
                                  getLocationString(leaderName)}));
                  }
              }
  
              if ("*".equals(o.getPrereqs()))
              {
                  if (_trailer == null)
                      _trailer = goal;
                  else
                  {
                      String trailerName = _trailer.getName();
                      _log.error(
                          HiveMind.format(
                              "Orderer.dupe-trailer",
                              new Object[] {
                                  StringUtils.capitalize(_objectType),
                                  o.getName(),
                                  HiveMind.getLocationString(o.getObject()),
                                  trailerName,
                                  getLocationString(trailerName)}));
                  }
              }
  
          }
      }
  
      private void addDependencies(WerkzProject project)
      {
          Iterator i = _orderingsList.iterator();
  
          while (i.hasNext())
          {
              ObjectOrdering o = (ObjectOrdering) i.next();
              String name = o.getName();
  
              Goal goal = project.getGoal(name);
  
              addDependencies(project, goal, o);
          }
      }
  
      private void addDependencies(WerkzProject project, Goal goal, 
ObjectOrdering orderable)
      {
  
          addPrecursors(project, goal, orderable.getPrereqs());
          addPostcursors(project, goal, orderable.getPostreqs());
  
          try
          {
              if (goal != _leader)
                  _leader.addPostcursor(goal);
  
              if (goal != _trailer)
                  _trailer.addPrecursor(goal);
          }
          catch (CyclicGoalChainException ex)
          {
              // This code is unreachable ... but nonetheless.
  
              String name = goal.getName();
  
              _log.error(
                  HiveMind.format(
                      "Orderer.dependency-cycle",
                      new Object[] { _objectType, name, 
getLocationString(name), ex.getMessage()}),
                  ex);
          }
      }
  
      private void addPrecursors(WerkzProject project, Goal goal, String 
prereqs)
      {
          if ("*".equals(prereqs))
              return;
  
          String[] names = split(prereqs);
  
          for (int i = 0; i < names.length; i++)
          {
              String prename = names[i];
  
              Goal pregoal = project.getGoal(prename);
  
              if (pregoal == null)
              {
                  String name = goal.getName();
                  _log.error(
                      HiveMind.format(
                          "Orderer.bad-dependency",
                          new Object[] { _objectType, prename, name, 
getLocationString(name)}));
              }
              else
              {
  
                  try
                  {
                      goal.addPrecursor(pregoal);
                  }
                  catch (CyclicGoalChainException ex)
                  {
                      String name = goal.getName();
                      _log.error(
                          HiveMind.format(
                              "Orderer.dependency-cycle",
                              new Object[] {
                                  _objectType,
                                  name,
                                  getLocationString(name),
                                  ex.getMessage()}),
                          ex);
                  }
  
              }
          }
      }
  
      private void addPostcursors(WerkzProject project, Goal goal, String 
postreqs)
      {
          if ("*".equals(postreqs))
              return;
  
          String[] names = split(postreqs);
  
          for (int i = 0; i < names.length; i++)
          {
              String postname = names[i];
  
              Goal postgoal = project.getGoal(postname);
  
              if (postgoal == null)
              {
                  String name = goal.getName();
                  _log.error(
                      HiveMind.format(
                          "Orderer.bad-dependency",
                          new Object[] { _objectType, postname, name, 
getLocationString(name)}));
              }
              else
              {
  
                  try
                  {
                      goal.addPostcursor(postgoal);
                  }
                  catch (CyclicGoalChainException ex)
                  {
                      String name = goal.getName();
                      _log.error(
                          HiveMind.format(
                              "Orderer.dependency-cycle",
                              new Object[] {
                                  _objectType,
                                  name,
                                  getLocationString(name),
                                  ex.getMessage()}),
                          ex);
                  }
  
              }
          }
  
      }
  
      private String getLocationString(String name)
      {
          return HiveMind.getLocationString(getOrderable(name).getObject());
      }
  
      /**
       * Splits an input string into a an array of strings.
       */
      private String[] split(String input)
      {
          if (input == null)
              return new String[0];
  
          return StringUtils.split(input, ',');
      }
  }
  
  
  
  1.4       +27 -4     
jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/RegistryBuilder.java
  
  Index: RegistryBuilder.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/RegistryBuilder.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- RegistryBuilder.java      15 Apr 2004 03:40:34 -0000      1.3
  +++ RegistryBuilder.java      18 Apr 2004 17:39:57 -0000      1.4
  @@ -25,6 +25,7 @@
   import java.util.Locale;
   import java.util.Map;
   
  +import org.apache.commons.lang.StringUtils;
   import org.apache.commons.logging.Log;
   import org.apache.commons.logging.LogFactory;
   import org.apache.hivemind.ApplicationRuntimeException;
  @@ -649,12 +650,14 @@
   
           ServiceExtensionPointImpl sep = (ServiceExtensionPointImpl) 
_servicePoints.get(pointId);
   
  +        String sourceModuleId = sourceModule.getModuleId();
  +
           if (sep == null)
           {
               LOG.error(
                   HiveMind.format(
                       "RegistryBuilder.unknown-service-extension-point",
  -                    sourceModule.getModuleId(),
  +                    sourceModuleId,
                       pointId,
                       id.getLocation()));
               return;
  @@ -665,13 +668,33 @@
           // Allow the factory id to be unqualified, to refer to an 
interceptor factory
           // service from within the same module.
   
  -        sic.setFactoryServiceId(qualify(sourceModule.getModuleId(), 
id.getFactoryServiceId()));
  +        sic.setFactoryServiceId(qualify(sourceModuleId, 
id.getFactoryServiceId()));
           sic.setLocation(id.getLocation());
  -        sic.setOrder(id.getOrder());
  +
  +        sic.setFollowingInterceptorIds(qualifyList(sourceModuleId, 
id.getBefore()));
  +        sic.setPrecedingInterceptorIds(qualifyList(sourceModuleId, 
id.getAfter()));
  +
           sic.setContributingModule(sourceModule);
           sic.setParameters(id.getParameters());
   
           sep.addInterceptorContribution(sic);
  +    }
  +
  +     /**
  +      * Qualifies a list of interceptor service ids provided for an 
interceptor
  +      * contribution.  The special value "*" is not qualified.
  +      */
  +    private String qualifyList(String sourceModuleId, String list)
  +    {
  +        if (HiveMind.isBlank(list) || list.equals("*"))
  +            return list;
  +
  +        String[] items = StringUtils.split(list, ',');
  +
  +        for (int i = 0; i < items.length; i++)
  +            items[i] = qualify(sourceModuleId, items[i]);
  +
  +        return StringUtils.join(items, ',');
       }
   
       /**
  
  
  
  1.2       +7 -11     
jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/ThreadedServiceModel.java
  
  Index: ThreadedServiceModel.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/ThreadedServiceModel.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ThreadedServiceModel.java 26 Feb 2004 23:07:40 -0000      1.1
  +++ ThreadedServiceModel.java 18 Apr 2004 17:39:57 -0000      1.2
  @@ -14,15 +14,13 @@
   
   package org.apache.hivemind.impl;
   
  -import org.apache.hivemind.service.ThreadCleanupListener;
  -import org.apache.hivemind.service.ThreadEventNotifier;
  -import org.apache.commons.logging.Log;
  -import org.apache.commons.logging.LogFactory;
   import org.apache.hivemind.ApplicationRuntimeException;
   import org.apache.hivemind.Discardable;
   import org.apache.hivemind.HiveMind;
   import org.apache.hivemind.Registry;
   import org.apache.hivemind.RegistryShutdownListener;
  +import org.apache.hivemind.service.ThreadCleanupListener;
  +import org.apache.hivemind.service.ThreadEventNotifier;
   
   /**
    * Like
  @@ -37,8 +35,6 @@
    */
   public final class ThreadedServiceModel extends AbstractServiceModelImpl
   {
  -    private static final Log LOG = 
LogFactory.getLog(ThreadedServiceModel.class);
  -
       /**
        * Name of a method in the deferred proxy that is used to obtain
        * the constructed service.
  @@ -92,9 +88,9 @@
        */
       public synchronized Object getServiceImplementation()
       {
  -     // _activeService will be null on first invocation; a good
  -     // time to create it, the proxy, and find the notifier.
  -     
  +        // _activeService will be null on first invocation; a good
  +        // time to create it, the proxy, and find the notifier.
  +
           if (_activeService == null)
           {
               _activeService = new ThreadLocal();
  @@ -166,7 +162,7 @@
               initializeCoreServiceImplementation(core);
   
               if (core instanceof RegistryShutdownListener)
  -                LOG.error(
  +                _log.error(
                       HiveMind.format(
                           "ThreadedServiceModel.registry-cleanup-ignored",
                           getServicePoint().getExtensionPointId()));
  
  
  
  1.2       +25 -11    
jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/ServiceInterceptorContributionImpl.java
  
  Index: ServiceInterceptorContributionImpl.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/ServiceInterceptorContributionImpl.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ServiceInterceptorContributionImpl.java   26 Feb 2004 23:07:40 -0000      
1.1
  +++ ServiceInterceptorContributionImpl.java   18 Apr 2004 17:39:57 -0000      
1.2
  @@ -38,18 +38,20 @@
       implements ServiceInterceptorContribution
   {
       private String _factoryServiceId;
  -    private int _order;
       private Module _contributingModule;
       private List _parameters;
       private List _convertedParameters;
       private ServiceInterceptorFactory _factory;
  +    private String _precedingInterceptorIds;
  +    private String _followingInterceptorIds;
   
       public String toString()
       {
           ToStringBuilder builder = new ToStringBuilder(this);
           builder.append("factoryServiceId", _factoryServiceId);
  -        builder.append("order", _order);
           builder.append("parameters", _parameters);
  +        builder.append("precedingInterceptorIds", _precedingInterceptorIds);
  +        builder.append("followingInterceptorIds", _followingInterceptorIds);
   
           return builder.toString();
       }
  @@ -59,20 +61,12 @@
           return _factoryServiceId;
       }
   
  -    public int getOrder()
  -    {
  -        return _order;
  -    }
   
       public void setFactoryServiceId(String string)
       {
           _factoryServiceId = string;
       }
   
  -    public void setOrder(int i)
  -    {
  -        _order = i;
  -    }
   
       public void createInterceptor(InterceptorStack stack)
       {
  @@ -116,6 +110,26 @@
       public void setParameters(List list)
       {
           _parameters = list;
  +    }
  +
  +    public String getFollowingInterceptorIds()
  +    {
  +        return _followingInterceptorIds;
  +    }
  +
  +    public String getPrecedingInterceptorIds()
  +    {
  +        return _precedingInterceptorIds;
  +    }
  +
  +    public void setFollowingInterceptorIds(String string)
  +    {
  +        _followingInterceptorIds = string;
  +    }
  +
  +    public void setPrecedingInterceptorIds(String string)
  +    {
  +        _precedingInterceptorIds = string;
       }
   
   }
  
  
  
  1.2       +33 -5     
jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/ConstructableServiceExtensionPoint.java
  
  Index: ConstructableServiceExtensionPoint.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/ConstructableServiceExtensionPoint.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ConstructableServiceExtensionPoint.java   26 Feb 2004 23:07:40 -0000      
1.1
  +++ ConstructableServiceExtensionPoint.java   18 Apr 2004 17:39:57 -0000      
1.2
  @@ -36,11 +36,39 @@
       public ServiceImplementationConstructor getServiceConstructor();
   
       /**
  -     * Returns a list of [EMAIL PROTECTED] 
org.apache.hivemind.ServiceInterceptorContribution}s, sorted
  -     * into ascending order. May return an empty list if there are no
  -     * interceptors, but won't return null.
  +     * Returns a list of [EMAIL PROTECTED] 
org.apache.hivemind.ServiceInterceptorContribution}s, 
  +     * ordered according to their dependencies.  May return null or an empty 
list.
  +     * 
  +     * <p>
  +     * Note that the order is tricky! To keep any error messages while 
ordering
  +     * the interceptors understandable, they are ordered according into 
runtime
  +     * execution order.  Example: If we want a logging interceptor
  +     * to operate before a security-check interceptor, we'll write the 
following
  +     * in the descriptor:
  +     * 
  +     * <pre>
  +     *   &lt;interceptor service-id="hivemind.LoggingInterceptor" 
before="*"/&gt;
  +     *   &lt;interceptor service-id="somepackage.SecurityInterceptor"/&gt;
  +     * </pre>
  +     * 
  +     * The <code>before</code> value for the first interceptor contribution
  +     * will be assigned to the contribution's
  +     * [EMAIL PROTECTED] 
org.apache.hivemind.ServiceInterceptorContribution#getFollowingInterceptorIds() 
followingInterceptorIds}
  +     * property, because all other interceptors (including the security 
interceptor)
  +     * should have their behavior follow the logging interceptor.
  +     * 
  +     * <p>
  +     * To get this behavior, the logging interceptor will delegate to the 
security
  +     * interceptor, and the security interceptor will delegate to
  +     * the core service implementation.
  +     * 
  +     * <p>
  +     * The trick is that interceptors are applied in reverse order: we start
  +     * with core service implementation, wrap it with the security 
interceptor, then
  +     * wrap that with the logging interceptor ... but that's an issue that 
applies
  +     * when building the interceptor stack around the core service 
implementation.
        */
  -    public List getSortedInterceptors();
  +    public List getOrderedInterceptorContributions();
   
       /**
        * Invoked by the ServiceModel when constuction information
  
  
  
  1.3       +25 -8     
jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/AbstractServiceModelImpl.java
  
  Index: AbstractServiceModelImpl.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/AbstractServiceModelImpl.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- AbstractServiceModelImpl.java     29 Feb 2004 20:57:08 -0000      1.2
  +++ AbstractServiceModelImpl.java     18 Apr 2004 17:39:57 -0000      1.3
  @@ -34,12 +34,20 @@
    */
   public abstract class AbstractServiceModelImpl implements ServiceModel
   {
  -    private static final Log LOG = 
LogFactory.getLog(AbstractServiceModelImpl.class);
  +    /**
  +     * This log is created from the log's service id, which is the 
appropriate
  +     * place to log any messages related to creating (or managing) the
  +     * service implementation, proxy, etc.  Subclasses should make
  +     * use of this Log as well.
  +     */
  +    protected final Log _log;
   
       private ConstructableServiceExtensionPoint _servicePoint;
   
       public AbstractServiceModelImpl(ConstructableServiceExtensionPoint 
servicePoint)
       {
  +        _log = LogFactory.getLog(servicePoint.getExtensionPointId());
  +
           _servicePoint = servicePoint;
       }
   
  @@ -61,16 +69,25 @@
   
       protected Object addInterceptors(Object core)
       {
  -        List interceptors = _servicePoint.getSortedInterceptors();
  +        List interceptors = 
_servicePoint.getOrderedInterceptorContributions();
   
  -        int count = interceptors.size();
  +        int count = interceptors == null ? 0 : interceptors.size();
   
           if (count == 0)
               return core;
   
           InterceptorStackImpl stack = new InterceptorStackImpl(_servicePoint, 
core);
   
  -        for (int i = 0; i < count; i++)
  +        // They are sorted into runtime execution order. Since we build from 
the
  +        // core service impl outwarads, we have to reverse the runtime 
execution
  +        // order to get the build order.
  +        // That is, if user expects interceptors in order A B C (perhaps 
using
  +        // the rules: A before B, C after B).
  +        // Then that's the order for interceptors list: A B C  
  +        // To get that runtime execution order, we wrap C around the core,
  +        // wrap B around C, and wrap A around B.
  +
  +        for (int i = count - 1; i >= 0; i--)
           {
               ServiceInterceptorContribution ic =
                   (ServiceInterceptorContribution) interceptors.get(i);
  @@ -96,8 +113,8 @@
          */
       protected Object constructCoreServiceImplementation()
       {
  -        if (LOG.isDebugEnabled())
  -            LOG.debug(
  +        if (_log.isDebugEnabled())
  +            _log.debug(
                   "Constructing core instance for service " + 
_servicePoint.getExtensionPointId());
   
           Class serviceType = _servicePoint.getServiceInterface();
  @@ -122,7 +139,7 @@
                   constructor.getLocation(),
                   null);
   
  -             HiveMind.setLocation(result, constructor.getLocation());
  +        HiveMind.setLocation(result, constructor.getLocation());
   
           return result;
       }
  
  
  
  1.2       +44 -7     
jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/ServiceExtensionPointImpl.java
  
  Index: ServiceExtensionPointImpl.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/impl/ServiceExtensionPointImpl.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ServiceExtensionPointImpl.java    26 Feb 2004 23:07:40 -0000      1.1
  +++ ServiceExtensionPointImpl.java    18 Apr 2004 17:39:57 -0000      1.2
  @@ -15,15 +15,19 @@
   package org.apache.hivemind.impl;
   
   import java.util.ArrayList;
  +import java.util.Iterator;
   import java.util.List;
   
   import org.apache.commons.lang.builder.ToStringBuilder;
  +import org.apache.commons.logging.Log;
  +import org.apache.commons.logging.LogFactory;
   import org.apache.hivemind.ApplicationRuntimeException;
   import org.apache.hivemind.ClassResolver;
   import org.apache.hivemind.HiveMind;
   import org.apache.hivemind.ServiceImplementationConstructor;
   import org.apache.hivemind.ServiceInterceptorContribution;
   import org.apache.hivemind.ServiceModel;
  +import org.apache.hivemind.order.Orderer;
   import org.apache.hivemind.schema.Schema;
   
   /**
  @@ -44,7 +48,7 @@
       private Class _serviceInterface;
       private ServiceImplementationConstructor _serviceConstructor;
       private List _interceptorContributions;
  -    private boolean _interceptorsSorted;
  +    private boolean _interceptorsOrdered;
       private Schema _parametersSchema;
       private ServiceModel _serviceModel;
       private ShutdownCoordinator _shutdownCoordinator;
  @@ -178,16 +182,49 @@
   
       // Hm. Does this need to be synchronized?
   
  -    public List getSortedInterceptors()
  +    public List getOrderedInterceptorContributions()
       {
  -        if (!_interceptorsSorted)
  +        if (!_interceptorsOrdered)
           {
  -
  -            _interceptorContributions = 
HiveMind.sortOrderables(_interceptorContributions);
  -            _interceptorsSorted = true;
  +            _interceptorContributions = orderInterceptors();
  +            _interceptorsOrdered = true;
           }
   
           return _interceptorContributions;
  +    }
  +
  +    private List orderInterceptors()
  +    {
  +        if (HiveMind.isEmpty(_interceptorContributions))
  +            return null;
  +
  +        // Any error logging should go to the extension point
  +        // we're constructing.
  +
  +        Log log = LogFactory.getLog(getExtensionPointId());
  +
  +        Orderer orderer =
  +            new Orderer(
  +                log,
  +                
HiveMind.getMessage("ServiceExtensionPointImpl.interceptor-contribution"));
  +
  +        Iterator i = _interceptorContributions.iterator();
  +        while (i.hasNext())
  +        {
  +            ServiceInterceptorContribution sic = 
(ServiceInterceptorContribution) i.next();
  +
  +                     // Sort them into runtime excecution order. When we 
build 
  +                     // the interceptor stack we'll apply them in reverse 
order,
  +                     // building outward from the core service 
implementation.
  +
  +            orderer.add(
  +                sic,
  +                sic.getFactoryServiceId(),
  +                sic.getPrecedingInterceptorIds(),
  +                sic.getFollowingInterceptorIds());
  +        }
  +
  +        return orderer.getOrderedObjects();
       }
   
       public ShutdownCoordinator getShutdownCoordinator()
  
  
  
  1.7       +15 -1     
jakarta-hivemind/framework/src/java/org/apache/hivemind/HiveMindMessages.properties
  
  Index: HiveMindMessages.properties
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/HiveMindMessages.properties,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- HiveMindMessages.properties       15 Apr 2004 03:40:34 -0000      1.6
  +++ HiveMindMessages.properties       18 Apr 2004 17:39:57 -0000      1.7
  @@ -1,3 +1,4 @@
  +
   # $Id$
   #
   # Copyright 2004 The Apache Software Foundation
  @@ -19,6 +20,8 @@
   no-such-service-extension-id=Service extension point {0} does not exist.
   no-such-service=Service {0} (implementing interface {1}) does not exist.
   
  +null-parameter-invalid=Parameter {0} may not be null.
  +
   unknown-location=unknown location
   
   wrong-factory-parameter-count=Service implementation factory {0} expects 
{1,choice,0#no parameters|1#one parameter|1<{1,number,integer} parameters} but 
received {2,choice,0#none|1#one|1<{2,number,integer}}.
  @@ -47,6 +50,8 @@
   ServiceExtensionPoint.factory-wrong-interface=Instance factory for service 
{0} returned {1} which does not implement the {2} interface declared by the 
extension point.
   ServiceExtensionPoint.recursive-service-build=A recursive call to construct 
service {0} has occured.  This indicates a cycle between one or more services 
or configurations.
   
  +ServiceExtensionPointImpl.interceptor-contribution=interceptor contribution
  +
   DescriptorParser.missing-resource=Unable to find resource {0}.
   DescriptorParser.error-reading-descriptor=Unable to read descriptor {0}: {1}
   DescriptorParser.unknown-attribute=Unknown attribute ''{0}'' in element {1} 
(at {2}).
  @@ -189,3 +194,12 @@
   
   ConstructorUtils.no-matching-constructor=Unable to find a constructor for 
class {0}.
   ConstructorUtils.invoke-failed=Failure invoking constructor for class {0} 
(at {1}): {2}
  +
  +# order package
  +
  +Orderer.duplicate-name={0} ''{1}'' (at {2}) duplicates previous value (at 
{3}) and is being ignored.
  +Orderer.bad-dependency=Unknown {0} dependency ''{1}'' (for ''{2}'', at {3}).
  +Orderer.dependency-cycle=Unable to order {0} ''{1}'' (at {2}) due to 
dependency cycle: {3}
  +Orderer.exception=Unable to order {0}s: {1}
  +Orderer.dupe-leader={0} ''{1}'' (at {2}) has been ordered first, conflicting 
with ''{3}'' (at {4}).
  +Orderer.dupe-trailer={0} ''{1}'' (at {2}) has been ordered last, conflicting 
with ''{3}'' (at {4}).
  \ No newline at end of file
  
  
  
  1.2       +20 -2     
jakarta-hivemind/framework/src/java/org/apache/hivemind/ServiceInterceptorContribution.java
  
  Index: ServiceInterceptorContribution.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/ServiceInterceptorContribution.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ServiceInterceptorContribution.java       26 Feb 2004 23:07:56 -0000      
1.1
  +++ ServiceInterceptorContribution.java       18 Apr 2004 17:39:57 -0000      
1.2
  @@ -21,7 +21,7 @@
    * @author Howard Lewis Ship
    * @version $Id$
    */
  -public interface ServiceInterceptorContribution extends Locatable, Orderable
  +public interface ServiceInterceptorContribution extends Locatable
   {    
       /**
        * Returns the id of the factory that creates the interceptor.
  @@ -34,4 +34,22 @@
        * Invoked to actually create the interceptor and push it onto the stack.
        */
        public void createInterceptor(InterceptorStack stack);
  +     
  +     /**
  +      * Returns a list interceptors service ids as a comma seperated list.
  +      * The behavior provided by these interceptors should
  +      * <em>precede</em> the behavior of this interceptor.
  +      * 
  +      * <p>Each service id is fully qualified. May return null.
  +      */
  +     
  +     public String getPrecedingInterceptorIds();
  +     
  +     /**
  +      * 
  +      * As [EMAIL PROTECTED] #getPrecedingInterceptorIds()}, but the 
indicating
  +      * interceptors's behavior should <em>follow</em> this interceptor's.
  +      */
  +     
  +     public String getFollowingInterceptorIds();
   }
  
  
  
  1.4       +28 -8     
jakarta-hivemind/framework/src/java/org/apache/hivemind/HiveMind.java
  
  Index: HiveMind.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/HiveMind.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- HiveMind.java     29 Feb 2004 20:57:09 -0000      1.3
  +++ HiveMind.java     18 Apr 2004 17:39:57 -0000      1.4
  @@ -16,6 +16,7 @@
   
   import java.text.MessageFormat;
   import java.util.ArrayList;
  +import java.util.Collection;
   import java.util.Collections;
   import java.util.Comparator;
   import java.util.List;
  @@ -273,13 +274,13 @@
           return !isBlank(string);
       }
   
  -     /**
  -      * Updates the location of an object, if the object implements
  -      * [EMAIL PROTECTED] LocationHolder}.
  -      * 
  -      * @param holder the object to be updated
  -      * @param location the location to assign to the holder object
  -      */
  +    /**
  +     * Updates the location of an object, if the object implements
  +     * [EMAIL PROTECTED] LocationHolder}.
  +     * 
  +     * @param holder the object to be updated
  +     * @param location the location to assign to the holder object
  +     */
       public static void setLocation(Object holder, Location location)
       {
           if (holder != null && holder instanceof LocationHolder)
  @@ -288,5 +289,24 @@
   
               lh.setLocation(location);
           }
  +    }
  +
  +    /**
  +     * Checks if the value (a constructor or method parameter) is null,
  +     * and throws an InvalidArgumentException if so.
  +     */
  +
  +    public static void checkNullParameter(String parameterName, Object value)
  +    {
  +        if (value == null)
  +            throw new 
IllegalArgumentException(format("null-parameter-invalid", parameterName));
  +    }
  +    
  +     /**
  +      * Returns true if the Collection is null or empty.
  +      */
  +    public static boolean isEmpty(Collection c)
  +    {
  +     return c == null || c.isEmpty();
       }
   }
  
  
  
  1.14      +8 -1      jakarta-hivemind/framework/project.xml
  
  Index: project.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/framework/project.xml,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- project.xml       5 Apr 2004 20:03:59 -0000       1.13
  +++ project.xml       18 Apr 2004 17:39:57 -0000      1.14
  @@ -78,6 +78,13 @@
        <version>2.3</version>
       </dependency>
       
  +    <!-- For handling ordering of operations (such as interceptors) -->
  +    
  +    <dependency>
  +     <id>werkz</id>  
  +     <version>1.0-beta-10</version>
  +    </dependency>
  +    
                <!-- For building the Ant tasks -->
                
       <dependency>
  
  
  
  1.1                  
jakarta-hivemind/framework/src/test/hivemind/test/order/TestOrderer.java
  
  Index: TestOrderer.java
  ===================================================================
  //  Copyright 2004 The Apache Software Foundation
  //
  // 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 hivemind.test.order;
  
  import hivemind.test.FrameworkTestCase;
  
  import java.util.List;
  
  import org.apache.hivemind.order.Orderer;
  
  /**
   * Tests for the [EMAIL PROTECTED] org.apache.hivemind.order.Orderer}.
   *
   * @author Howard Lewis Ship
   * @version $Id: TestOrderer.java,v 1.1 2004/04/18 17:39:57 hlship Exp $
   */
  public class TestOrderer extends FrameworkTestCase
  {
      public void testNoDependencies() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "fred", null, null);
          o.add("BARNEY", "barney", null, null);
          o.add("WILMA", "wilma", null, null);
          o.add("BETTY", "betty", null, null);
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "FRED", "BARNEY", "WILMA", "BETTY" }, 
l);
      }
  
      public void testPrereq() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "fred", "wilma", null);
          o.add("BARNEY", "barney", "betty", null);
          o.add("BETTY", "betty", null, null);
          o.add("WILMA", "wilma", null, null);
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "WILMA", "FRED", "BETTY", "BARNEY" }, 
l);
      }
  
      public void testPostreq() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "fred", null, "barney,wilma");
          o.add("BARNEY", "barney", null, "betty");
          o.add("BETTY", "betty", null, null);
          o.add("WILMA", "wilma", null, null);
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "FRED", "BARNEY", "BETTY", "WILMA" }, 
l);
      }
  
      public void testPrePostreq() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "fred", null, "barney,wilma");
          o.add("BARNEY", "barney", "wilma", "betty");
          o.add("BETTY", "betty", null, null);
          o.add("WILMA", "wilma", null, null);
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "FRED", "WILMA", "BARNEY", "BETTY" }, 
l);
      }
  
      public void testUnknownPrereq() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "fred", "charlie", "barney,wilma");
          o.add("BARNEY", "barney", "wilma", "betty");
          o.add("BETTY", "betty", null, null);
          o.add("WILMA", "wilma", null, null);
  
          interceptLogging();
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "FRED", "WILMA", "BARNEY", "BETTY" }, 
l);
  
          assertLoggedMessage("Unknown cartoon character dependency 'charlie' 
(for 'fred', at unknown location).");
  
      }
  
      public void testUnknownPostreq() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "fred", null, "barney,wilma");
          o.add("BARNEY", "barney", "wilma", "betty");
          o.add("BETTY", "betty", null, "dino");
          o.add("WILMA", "wilma", null, null);
  
          interceptLogging();
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "FRED", "WILMA", "BARNEY", "BETTY" }, 
l);
  
          assertLoggedMessage("Unknown cartoon character dependency 'dino' (for 
'betty', at unknown location).");
      }
  
      public void testCyclePre() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "fred", "wilma", null);
          o.add("BARNEY", "barney", "betty", null);
          o.add("BETTY", "betty", "fred", null);
          o.add("WILMA", "wilma", "barney", null);
  
          interceptLogging();
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "WILMA", "FRED", "BETTY", "BARNEY" }, 
l);
  
          assertLoggedMessage(
              "Unable to order cartoon character 'wilma' (at unknown location) 
due to dependency cycle:"
                  + " A cycle has been detected from the initial goal [wilma]");
      }
  
      public void testCyclePost() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("WILMA", "wilma", null, "betty");
          o.add("FRED", "fred", null, "barney");
          o.add("BARNEY", "barney", null, "wilma");
          o.add("BETTY", "betty", null, "fred");
  
          interceptLogging();
  
          List l = o.getOrderedObjects();
          assertListsEqual(new Object[] { "FRED", "BARNEY", "WILMA", "BETTY" }, 
l);
  
          assertLoggedMessage(
              "Unable to order cartoon character 'betty' (at unknown location) 
due to dependency cycle:"
                  + " A cycle has been detected from the initial goal [fred]");
      }
  
      public void testDupe() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "flintstone", null, null);
          o.add("BARNEY", "rubble", null, null);
  
          interceptLogging();
  
          o.add("WILMA", "flintstone", null, null);
  
          assertLoggedMessage("Cartoon character 'flintstone' (at unknown 
location) duplicates previous value (at unknown location) and is being 
ignored.");
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "FRED", "BARNEY" }, l);
      }
  
      public void testPreStar() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "fred", "*", null);
          o.add("BARNEY", "barney", "betty", null);
          o.add("WILMA", "wilma", "betty", null);
          o.add("BETTY", "betty", null, null);
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "BETTY", "BARNEY", "WILMA", "FRED" }, 
l);
      }
  
      public void testPreStartDupe() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "fred", "*", null);
          o.add("BARNEY", "barney", "*", null);
          o.add("WILMA", "wilma", "betty", null);
          o.add("BETTY", "betty", null, null);
  
          interceptLogging();
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "BARNEY", "BETTY", "WILMA", "FRED" }, 
l);
  
          assertLoggedMessage(
              "Cartoon character 'barney' (at unknown location) has been 
ordered "
                  + "last, conflicting with 'fred' (at unknown location).");
      }
  
      public void testPostStar() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "fred", null, "wilma");
          o.add("BARNEY", "barney", null, "*");
          o.add("WILMA", "wilma", null, "betty");
          o.add("BETTY", "betty", null, null);
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "BARNEY", "FRED", "WILMA", "BETTY" }, 
l);
      }
  
      public void testPostStarDupe() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          o.add("FRED", "fred", null, "wilma");
          o.add("BARNEY", "barney", null, "*");
          o.add("WILMA", "wilma", null, "*");
          o.add("BETTY", "betty", null, null);
  
          interceptLogging();
  
          List l = o.getOrderedObjects();
  
          assertListsEqual(new Object[] { "BARNEY", "FRED", "WILMA", "BETTY" }, 
l);
  
          assertLoggedMessage(
              "Cartoon character 'wilma' (at unknown location) has been ordered 
"
                  + "first, conflicting with 'barney' (at unknown location).");
      }
  
      public void testNoObjects() throws Exception
      {
          Orderer o = new Orderer("cartoon character");
  
          List l = o.getOrderedObjects();
  
          assertEquals(0, l.size());
      }
  
  }
  
  
  
  1.4       +3 -3      
jakarta-hivemind/framework/src/test/hivemind/test/services/TestShutdown.java
  
  Index: TestShutdown.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/services/TestShutdown.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- TestShutdown.java 26 Feb 2004 23:07:36 -0000      1.3
  +++ TestShutdown.java 18 Apr 2004 17:39:58 -0000      1.4
  @@ -106,7 +106,7 @@
   
           Runnable s = (Runnable) 
r.getService("hivemind.test.services.Singleton", Runnable.class);
   
  -        interceptLogging("hivemind.test");
  +        interceptLogging("hivemind.test.services.Singleton");
   
           s.run();
   
  @@ -123,7 +123,7 @@
   
           Runnable s = (Runnable) 
r.getService("hivemind.test.services.Primitive", Runnable.class);
   
  -        interceptLogging("hivemind.test");
  +        interceptLogging("hivemind.test.services.Primitive");
   
           s.run();
   
  
  
  
  1.4       +2 -2      
jakarta-hivemind/framework/src/test/hivemind/test/services/TestPooledServiceModel.java
  
  Index: TestPooledServiceModel.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/services/TestPooledServiceModel.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- TestPooledServiceModel.java       26 Feb 2004 23:07:36 -0000      1.3
  +++ TestPooledServiceModel.java       18 Apr 2004 17:39:58 -0000      1.4
  @@ -39,7 +39,7 @@
           StringHolder s =
               (StringHolder) r.getService("hivemind.test.services.Managed", 
StringHolder.class);
   
  -        interceptLogging("hivemind.test");
  +        interceptLogging("hivemind.test.services.Managed");
   
           assertNull(s.getValue());
   
  
  
  
  1.8       +6 -2      
jakarta-hivemind/framework/src/test/hivemind/test/services/TestThreadedModel.java
  
  Index: TestThreadedModel.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/services/TestThreadedModel.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- TestThreadedModel.java    26 Feb 2004 23:07:36 -0000      1.7
  +++ TestThreadedModel.java    18 Apr 2004 17:39:58 -0000      1.8
  @@ -72,12 +72,14 @@
   
           assertLoggedMessages(
               new String[] {
  +                "Constructing core instance for service 
hivemind.test.services.StringHolder",
                   "BEGIN getValue()",
                   "END getValue() [<null>]",
                   "BEGIN setValue(fred)",
                   "END setValue()",
                   "BEGIN getValue()",
                   "END getValue() [fred]",
  +                "Constructing core instance for service 
hivemind.test.services.StringHolder",
                   "BEGIN getValue()",
                   "END getValue() [<null>]" });
   
  @@ -121,12 +123,14 @@
   
           assertLoggedMessages(
               new String[] {
  +                "Constructing core instance for service 
hivemind.test.services.StringHolder",
                   "BEGIN getValue()",
                   "END getValue() [<null>]",
                   "BEGIN setValue(fred)",
                   "END setValue()",
                   "BEGIN getValue()",
                   "END getValue() [fred]",
  +                "Constructing core instance for service 
hivemind.test.services.StringHolder",
                   "BEGIN getValue()",
                   "END getValue() [<null>]",
                   "BEGIN setValue(barney)",
  @@ -156,7 +160,7 @@
                   "hivemind.test.services.ThreadedRegistryShutdown",
                   StringHolder.class);
   
  -        interceptLogging();
  +        interceptLogging("hivemind.test.services");
   
           h.setValue("foo");
   
  @@ -183,7 +187,7 @@
                   "hivemind.ThreadEventNotifier",
                   ThreadEventNotifier.class);
   
  -        interceptLogging("hivemind");
  +        interceptLogging("hivemind.test.services");
   
           n.fireThreadCleanup();
   
  
  
  
  1.3       +2 -2      
jakarta-hivemind/framework/src/test/hivemind/test/services/AddSimpleInterceptors1.xml
  
  Index: AddSimpleInterceptors1.xml
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/services/AddSimpleInterceptors1.xml,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- AddSimpleInterceptors1.xml        18 Sep 2003 19:00:59 -0000      1.2
  +++ AddSimpleInterceptors1.xml        18 Apr 2004 17:39:58 -0000      1.3
  @@ -4,6 +4,6 @@
        id="hivemind.test.services.add1" 
        version="1.0.0">
        <implementation service-id="hivemind.test.services.Simple">
  -       <interceptor service-id="hivemind.test.services.tracker.Wilma" 
order="100"/>
  +       <interceptor service-id="hivemind.test.services.tracker.Wilma" 
before="hivemind.test.services.tracker.Barney"/>
        </implementation>
   </module>
  
  
  
  1.3       +3 -3      
jakarta-hivemind/framework/src/test/hivemind/test/services/AddSimpleInterceptors2.xml
  
  Index: AddSimpleInterceptors2.xml
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/services/AddSimpleInterceptors2.xml,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- AddSimpleInterceptors2.xml        18 Sep 2003 19:00:59 -0000      1.2
  +++ AddSimpleInterceptors2.xml        18 Apr 2004 17:39:58 -0000      1.3
  @@ -4,7 +4,7 @@
        id="hivemind.test.services.add2" 
        version="1.0.0">
        <implementation service-id="hivemind.test.services.Simple">
  -       <interceptor service-id="hivemind.test.services.tracker.Barney" 
order="50"/>
  -       <interceptor service-id="hivemind.test.services.tracker.Fred" 
order="10"/>
  +       <interceptor service-id="hivemind.test.services.tracker.Barney"/>
  +       <interceptor service-id="hivemind.test.services.tracker.Fred" 
after="hivemind.test.services.tracker.Barney"/>
        </implementation>
   </module>
  
  
  
  1.11      +17 -9     
jakarta-hivemind/framework/src/test/hivemind/test/services/TestServices.java
  
  Index: TestServices.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/services/TestServices.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- TestServices.java 7 Apr 2004 15:18:12 -0000       1.10
  +++ TestServices.java 18 Apr 2004 17:39:58 -0000      1.11
  @@ -84,10 +84,10 @@
           assertEquals(2, CountFactory.getCount());
       }
   
  -     // Note: this works when run by Maven, but for some reason
  -     // is failing inside Eclipse.  It appears the be a Log4J 
  -     // configuration problem ... but I have no idea why.
  -     
  +    // Note: this works when run by Maven, but for some reason
  +    // is failing inside Eclipse.  It appears the be a Log4J 
  +    // configuration problem ... but I have no idea why.
  +
       public void testInterceptorSort() throws Exception
       {
           Registry r =
  @@ -134,7 +134,11 @@
   
           s.add(5, 3);
   
  -        assertLoggedMessages(new String[] { "BEGIN add(5, 3)", "END add() 
[8]" });
  +        assertLoggedMessages(
  +            new String[] {
  +                "Constructing core instance for service 
hivemind.test.services.Demo",
  +                "BEGIN add(5, 3)",
  +                "END add() [8]" });
   
           s.noResult();
   
  @@ -214,7 +218,7 @@
   
           ToString ts = (ToString) 
r.getService("hivemind.test.services.ToString", ToString.class);
   
  -        interceptLogging("hivemind.test");
  +        interceptLogging("hivemind.test.services.ToString");
   
           assertEquals("ToStringImpl of toString()", ts.toString());
   
  @@ -237,7 +241,7 @@
   
           assertEquals("hivemind.test.services.BuilderAccess", 
s.getExtensionPointId());
   
  -        interceptLogging("hivemind");
  +        interceptLogging("hivemind.test.services.BuilderAccess");
   
           s.logMessage("This is a test.");
   
  @@ -321,7 +325,11 @@
   
           // Only fred and wilma should be logged.
   
  -        assertLoggedMessages(new String[] { "BEGIN fred()", "BEGIN wilma()" 
});
  +        assertLoggedMessages(
  +            new String[] {
  +                "Constructing core instance for service 
hivemind.test.services.Bedrock",
  +                "BEGIN fred()",
  +                "BEGIN wilma()" });
       }
   
   }
  
  
  
  1.3       +20 -7     
jakarta-hivemind/framework/src/java/org/apache/hivemind/parse/InterceptorDescriptor.java
  
  Index: InterceptorDescriptor.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/parse/InterceptorDescriptor.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- InterceptorDescriptor.java        14 Apr 2004 21:11:51 -0000      1.2
  +++ InterceptorDescriptor.java        18 Apr 2004 17:39:58 -0000      1.3
  @@ -24,20 +24,33 @@
    */
   public final class InterceptorDescriptor extends 
AbstractServiceInvocationDescriptor
   {
  -    private int _order;
  +    private String _before;
  +    private String _after;
   
  -    public int getOrder()
  +    public String getAfter()
       {
  -        return _order;
  +        return _after;
       }
   
  -    public void setOrder(int i)
  +    public String getBefore()
       {
  -        _order = i;
  +        return _before;
  +    }
  +
  +    public void setAfter(String string)
  +    {
  +        _after = string;
  +    }
  +
  +    public void setBefore(String string)
  +    {
  +        _before = string;
       }
   
       protected void extendDescription(ToStringBuilder builder)
       {
  -        builder.append("order", _order);
  +        builder.append("before", _before);
  +        builder.append("after", _after);
       }
  +
   }
  
  
  
  1.2       +3 -2      
jakarta-hivemind/framework/src/java/org/apache/hivemind/parse/DescriptorParser.properties
  
  Index: DescriptorParser.properties
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/parse/DescriptorParser.properties,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- DescriptorParser.properties       15 Apr 2004 03:40:34 -0000      1.1
  +++ DescriptorParser.properties       18 Apr 2004 17:39:58 -0000      1.2
  @@ -36,7 +36,8 @@
   required.create-instance.class=true
   required.create-instance.model=false
   
  -required.interceptor.order=false
  +required.interceptor.before=false
  +required.interceptor.after=false
   required.interceptor.service-id=true
   
   required.element.name=true
  
  
  
  1.7       +3 -3      
jakarta-hivemind/framework/src/java/org/apache/hivemind/parse/DescriptorParser.java
  
  Index: DescriptorParser.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/java/org/apache/hivemind/parse/DescriptorParser.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- DescriptorParser.java     15 Apr 2004 03:40:34 -0000      1.6
  +++ DescriptorParser.java     18 Apr 2004 17:39:58 -0000      1.7
  @@ -1052,8 +1052,8 @@
   
               id.setFactoryServiceId(getAttribute("service-id"));
   
  -            if (isAttribute("order"))
  -                id.setOrder(getIntAttribute("order"));
  +                     id.setBefore(getAttribute("before"));
  +                     id.setAfter(getAttribute("after"));
   
               sd.addInterceptor(id);
   
  
  
  
  1.11      +14 -1     
jakarta-hivemind/framework/src/test/hivemind/test/TestMisc.java
  
  Index: TestMisc.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/TestMisc.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- TestMisc.java     28 Feb 2004 00:34:37 -0000      1.10
  +++ TestMisc.java     18 Apr 2004 17:39:58 -0000      1.11
  @@ -201,4 +201,17 @@
   
           assertEquals(System.getProperty("user.home"), 
s.valueForSymbol("user.home"));
       }
  +
  +    public void testCheckNullParameter()
  +    {
  +        try
  +        {
  +            HiveMind.checkNullParameter("someParameter", null);
  +            unreachable();
  +        }
  +        catch (IllegalArgumentException ex)
  +        {
  +            assertEquals("Parameter someParameter may not be null.", 
ex.getMessage());
  +        }
  +    }
   }
  
  
  
  1.4       +8 -2      
jakarta-hivemind/framework/src/test/hivemind/test/services/impl/TrackerFactory.java
  
  Index: TrackerFactory.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/services/impl/TrackerFactory.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- TrackerFactory.java       26 Feb 2004 23:07:35 -0000      1.3
  +++ TrackerFactory.java       18 Apr 2004 17:39:58 -0000      1.4
  @@ -23,6 +23,7 @@
   import org.apache.hivemind.InterceptorStack;
   import org.apache.hivemind.Module;
   import org.apache.hivemind.ServiceInterceptorFactory;
  +import org.apache.hivemind.service.ClassFabUtils;
   
   /**
    * Used with the unit tests.
  @@ -47,7 +48,12 @@
   
           public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable
           {
  -            _list.add(_name + ":" + method.getName());
  +             // For some reason, testing inside Eclipse, logging is getting 
turned
  +             // on and toString() is getting invoked ... doesn't happen using
  +             // command-line testing, though.  A mystery.
  +             
  +            if (!ClassFabUtils.isToString(method))
  +             _list.add(_name + ":" + method.getName());
   
               return method.invoke(_inner, args);
           }
  
  
  
  1.8       +3 -3      
jakarta-hivemind/framework/src/test/hivemind/test/parse/TestDescriptorParser.java
  
  Index: TestDescriptorParser.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/parse/TestDescriptorParser.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- TestDescriptorParser.java 26 Feb 2004 23:07:50 -0000      1.7
  +++ TestDescriptorParser.java 18 Apr 2004 17:39:58 -0000      1.8
  @@ -176,11 +176,11 @@
   
           InterceptorDescriptor id = (InterceptorDescriptor) l.get(0);
           assertEquals("MyInterceptor", id.getFactoryServiceId());
  -        assertEquals(1000, id.getOrder());
  +        assertEquals("OtherInterceptor", id.getBefore());
   
           id = (InterceptorDescriptor) l.get(1);
           assertEquals("OtherInterceptor", id.getFactoryServiceId());
  -        assertEquals(0, id.getOrder());
  +        assertEquals("MyInterceptor", id.getAfter());
       }
   
       public void testImplementation() throws Exception
  
  
  
  1.4       +3 -3      
jakarta-hivemind/framework/src/test/hivemind/test/parse/GenericModule.xml
  
  Index: GenericModule.xml
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/parse/GenericModule.xml,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- GenericModule.xml 26 Feb 2004 23:07:50 -0000      1.3
  +++ GenericModule.xml 18 Apr 2004 17:39:58 -0000      1.4
  @@ -86,8 +86,8 @@
                
                <create-instance class="package.impl.MyServiceImpl"/>
                
  -             <interceptor service-id="MyInterceptor" order="1000"/>
  -             <interceptor service-id="OtherInterceptor"/>
  +             <interceptor service-id="MyInterceptor" 
before="OtherInterceptor"/>
  +             <interceptor service-id="OtherInterceptor" 
after="MyInterceptor"/>
                
     </service-point>
     
  
  
  
  1.4       +2 -2      
jakarta-hivemind/framework/src/test/hivemind/test/parse/BadElement.xml
  
  Index: BadElement.xml
  ===================================================================
  RCS file: 
/home/cvs/jakarta-hivemind/framework/src/test/hivemind/test/parse/BadElement.xml,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- BadElement.xml    26 Feb 2004 23:07:50 -0000      1.3
  +++ BadElement.xml    18 Apr 2004 17:39:58 -0000      1.4
  @@ -86,7 +86,7 @@
                
                <create-instance class="package.impl.MyServiceImpl"/>
                
  -             <interceptor service-id="MyInterceptor" order="1000"/>
  +             <interceptor service-id="MyInterceptor"/>
                <interceptor service-id="OtherInterceptor"/>
                
     </service-point>
  
  
  
  1.3       +52 -41    jakarta-hivemind/xdocs/images/InterceptorStack.png
  
        <<Binary file>>
  
  
  1.34      +31 -16    jakarta-hivemind/xdocs/services.xml
  
  Index: services.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/xdocs/services.xml,v
  retrieving revision 1.33
  retrieving revision 1.34
  diff -u -r1.33 -r1.34
  --- services.xml      7 Apr 2004 20:03:28 -0000       1.33
  +++ services.xml      18 Apr 2004 17:39:58 -0000      1.34
  @@ -77,7 +77,9 @@
                        </p>
                        
                        <p>
  -                     HiveMind uses a system of <em>proxies</em> for most of 
the service models. Proxies
  +                     HiveMind uses a system of <em>proxies</em> for most of 
the service models (all except the primitive 
  +                     service model, which primarily exists to bootstrap the 
core HiveMind services
  +                     used by other services). Proxies
                        are objects that implement the service interface and 
take care of details such as constructing
                        the actual implementation of a service on the fly. 
These lifecycle issues are kept hidden
                        from your code behind the proxies.
  @@ -203,23 +205,36 @@
                                        
                                <p>A service extension point may have any 
number of interceptor
                                        contributions. If the order in which 
interceptors are applied is
  -                                     important, then the optional order 
attribute can be specified; it is
  -                                     used to sort the interceptors into the 
order in which they will be
  -                                     applied.</p>
  +                                     important, then the optional 
<code>before</code> 
  +                                     and <code>after</code> attributes can 
be specified.                     
  +                                     
  +                                     </p>
                                        
                                <img src="images/InterceptorStack.png"/>
                                
  -                             <p>
  -                             The lowest order ("Security", with an order of 
100) is applied first, wrapping around
  -                             the core implementation.  Then comes 
"Performance" and last "Logging".  When a client
  -                             invokes a method, it is really invoking it on 
the "Logging" interceptor, which then re-invokes the method
  -                             on the "Performance" interceptor, and so in, 
down to the core implementation.  Each interceptor
  -                             can perjakarta-hivemindform work before and/or 
after re-invoking the method on the next layer
  -                             (as well as catch exceptions thrown by the more 
inner objects).  In this example,
  -                             "Logging" was given a high order so that it 
wouldn't affect the "Performance" interceptor.  Security
  -                             was given a lower order than "Performance" so 
that the time for security checks would be included in the
  -                             "Performance" analysis.
  -                             </p>
  +<p>
  +In this example, is was desired that any method logging occur first, before 
the other
  +interceptors.  This ensures that the time taken to log method entry and exit 
is not
  +included in the performance statistics (gathered by the performance 
interceptor).
  +To ensure that the logging interceptor is the first, or earliest, 
interceptor, the special value
  +<code>*</code>       (rather than a list of interceptor service ids) is 
given for its <code>before</code>
  +attribute (within the &interceptor; element). This forces the logging 
interceptor to the front of the list
  +(however, only a single interceptor may be so designated).
  +</p>
  +
  +<p>
  +Likewise, the security checks should occur last, after logging and after 
performance; this is accomplished
  +by setting the <cde>after</cde>       attribute to <code>*</code>.  The 
performance interceptor naturally falls 
  +between the two.
  +</p>
  +
  +<p>
  +This is about as complex as an interceptor stack is likely to grow.  
However, through the use of explicit 
  +dependencies, almost any arraingment of interceptors is possible ... even 
when different modules contribute
  +the interceptors.    
  +</p>
  +
  +
                                
                                <p>
                                Interceptors implement the 
<code>toString()</code>      method to provide a useful identification for the 
interceptor,
  
  
  
  1.35      +31 -5     jakarta-hivemind/xdocs/descriptor.xml
  
  Index: descriptor.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/xdocs/descriptor.xml,v
  retrieving revision 1.34
  retrieving revision 1.35
  diff -u -r1.34 -r1.35
  --- descriptor.xml    15 Apr 2004 03:40:34 -0000      1.34
  +++ descriptor.xml    18 Apr 2004 17:39:58 -0000      1.35
  @@ -329,11 +329,18 @@
                                                within the same module, or a 
fully qualified id. </td>
                                </tr>
                                <tr>
  -                                     <td>order</td>
  -                                     <td>number</td>
  +                                     <td>before</td>
  +                                     <td>string</td>
                                        <td>no</td>
  -                                     <td>A numeric value used to set the 
order in which interceptors are
  -                                             applied. Lowest orders go 
first; the default value is 0.</td>
  +                                     <td>A list of interceptors whose 
behavior should come later in execution
  +                                             than this interceptor.</td>
  +                             </tr>
  +                                                             <tr>
  +                                     <td>after</td>
  +                                     <td>string</td>
  +                                     <td>no</td>
  +                                     <td>A list of interceptors whose 
behavior should come earlier in execution
  +                                             than this interceptor.</td>
                                </tr>
                        </table>
                
  @@ -342,6 +349,25 @@
   As with &invoke-factory;, parameters to the interceptor factory are enclosed 
by
   the &_interceptor; element. The service interceptor factory will decode the
   parameters using its &parameters-schema; schema definition.
  +</p>
  +
  +<p>
  +Interceptor ordering is based on dependencies; each interceptor can 
identify, by interceptor service id,
  +other interceptors.  Interceptors in the <code>before</code> list are 
deferred until after this interceptor.
  +Likewise, this interceptor is deferred until after all interceptors in the 
<code>after</code> list.
  +</p>
  +
  +<blockquote>
  +     The <code>after</code>   dependencies will look familar to anyone who 
has used
  +     <a href="http://ant.apache.org/";>Ant</a> or any version of Make.  
<code>before</code> dependencies
  +     are simply the opposite.
  +</blockquote>
  +
  +<p>
  +The value for <code>before</code>     or <code>after</code> is a list of 
service ids seperated by commas.
  +Service ids may be unqualified if they are within the same module. 
Alternately, the fixed value
  +<code>*</code> may be used instead of a list: this indicates that the 
interceptor should be ordered
  +absolutely first or absolutely last.
   </p>
   
                </section>
  
  
  
  1.3       +85 -84    jakarta-hivemind/src/images/InterceptorStack.psp
  
        <<Binary file>>
  
  
  1.25      +7 -3      jakarta-hivemind/src/xsl/hivemind.xsl
  
  Index: hivemind.xsl
  ===================================================================
  RCS file: /home/cvs/jakarta-hivemind/src/xsl/hivemind.xsl,v
  retrieving revision 1.24
  retrieving revision 1.25
  diff -u -r1.24 -r1.25
  --- hivemind.xsl      7 Apr 2004 20:04:05 -0000       1.24
  +++ hivemind.xsl      18 Apr 2004 17:39:58 -0000      1.25
  @@ -484,9 +484,13 @@
                        <span class="tag">&lt;interceptor</span>
                        <span class="attribute"> 
service-id</span>="<xsl:apply-templates select="/registry/module/[EMAIL 
PROTECTED] = current()/@service-id]" mode="link"/>"
                                
  -                     <xsl:if test="@order">
  -                       <span class="attribute"> order</span>="<xsl:value-of 
select="@order"/>"
  +                     <xsl:if test="@before">
  +                       <span class="attribute"> before</span>="<xsl:value-of 
select="@before"/>"
                        </xsl:if> 
  +                     
  +                     <xsl:if test="@after">
  +                       <span class="attribute"> after</span>="<xsl:value-of 
select="@after"/>"
  +                     </xsl:if>                       
   
                  <xsl:choose>
                        <xsl:when test="*">
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to