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>
+ * <interceptor service-id="hivemind.LoggingInterceptor"
before="*"/>
+ * <interceptor service-id="somepackage.SecurityInterceptor"/>
+ * </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 ¶meters-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"><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]