Revision: 431
http://svn.sourceforge.net/stripes/?rev=431&view=rev
Author: tfenne
Date: 2006-10-07 10:50:55 -0700 (Sat, 07 Oct 2006)
Log Message:
-----------
Fix for STS-186: before/after methods should be able to specify which events to
execute on.
Modified Paths:
--------------
trunk/stripes/src/net/sourceforge/stripes/action/After.java
trunk/stripes/src/net/sourceforge/stripes/action/Before.java
trunk/stripes/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptor.java
trunk/stripes/src/net/sourceforge/stripes/controller/DispatcherHelper.java
trunk/stripes/src/net/sourceforge/stripes/util/CollectionUtil.java
trunk/tests/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptorTests.java
trunk/tests/src/net/sourceforge/stripes/controller/MapBindingTests.java
trunk/tests/src/net/sourceforge/stripes/util/CollectionUtilTest.java
Modified: trunk/stripes/src/net/sourceforge/stripes/action/After.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/action/After.java 2006-10-06
02:16:47 UTC (rev 430)
+++ trunk/stripes/src/net/sourceforge/stripes/action/After.java 2006-10-07
17:50:55 UTC (rev 431)
@@ -66,5 +66,13 @@
@Documented
public @interface After {
/** One or more lifecycle stages after which the method should be
called. */
- LifecycleStage[] value() default LifecycleStage.EventHandling;
+ LifecycleStage[] stages() default LifecycleStage.EventHandling;
+
+ /**
+ * Allows the method to be restricted to one or more events. By default
the method will
+ * be executed on all events. Can be used to specify one or more events to
apply the method
+ * to (e.g. on={"save", "update"}), or to specify one or more events
<i>not</i> to apply
+ * the method to (e.g. on="!delete").
+ */
+ String[] on() default {};
}
Modified: trunk/stripes/src/net/sourceforge/stripes/action/Before.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/action/Before.java
2006-10-06 02:16:47 UTC (rev 430)
+++ trunk/stripes/src/net/sourceforge/stripes/action/Before.java
2006-10-07 17:50:55 UTC (rev 431)
@@ -67,5 +67,13 @@
@Documented
public @interface Before {
/** One or more lifecycle stages before which the method should be
called. */
- LifecycleStage[] value() default LifecycleStage.EventHandling;
+ LifecycleStage[] stages() default LifecycleStage.EventHandling;
+
+ /**
+ * Allows the method to be restricted to one or more events. By default
the method will
+ * be executed on all events. Can be used to specify one or more events to
apply the method
+ * to (e.g. on={"save", "update"}), or to specify one or more events
<i>not</i> to apply
+ * the method to (e.g. on="!delete").
+ */
+ String[] on() default {};
}
Modified:
trunk/stripes/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptor.java
===================================================================
---
trunk/stripes/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptor.java
2006-10-06 02:16:47 UTC (rev 430)
+++
trunk/stripes/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptor.java
2006-10-07 17:50:55 UTC (rev 431)
@@ -18,8 +18,10 @@
import net.sourceforge.stripes.action.After;
import net.sourceforge.stripes.action.Before;
import net.sourceforge.stripes.action.Resolution;
+import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.util.Log;
import net.sourceforge.stripes.util.ReflectUtil;
+import net.sourceforge.stripes.util.CollectionUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
@@ -87,6 +89,8 @@
*/
public Resolution intercept(ExecutionContext context) throws Exception {
LifecycleStage stage = context.getLifecycleStage();
+ ActionBeanContext abc = context.getActionBeanContext();
+ String event = abc == null ? null : abc.getEventName();
Resolution resolution = null;
// Run @Before methods, as long as there's a bean to run them on
@@ -96,9 +100,12 @@
List<Method> beforeMethods =
filterMethods.getBeforeMethods(stage);
for (Method method : beforeMethods) {
- resolution = invoke(bean, method, stage, Before.class);
- if (resolution != null) {
- return resolution;
+ String[] on = method.getAnnotation(Before.class).on();
+ if (event == null || CollectionUtil.applies(on, event)) {
+ resolution = invoke(bean, method, stage, Before.class);
+ if (resolution != null) {
+ return resolution;
+ }
}
}
}
@@ -113,9 +120,12 @@
Resolution overrideResolution = null;
for (Method method : afterMethods) {
- overrideResolution = invoke(bean, method, stage, After.class);
- if (overrideResolution != null) {
- return overrideResolution;
+ String[] on = method.getAnnotation(After.class).on();
+ if (event == null || CollectionUtil.applies(on, event)) {
+ overrideResolution = invoke(bean, method, stage, After.class);
+ if (overrideResolution != null) {
+ return overrideResolution;
+ }
}
}
@@ -215,12 +225,12 @@
if (method.isAnnotationPresent(Before.class)) {
Before annotation =
method.getAnnotation(Before.class);
- filterMethods.addBeforeMethod(annotation.value(),
method);
+ filterMethods.addBeforeMethod(annotation.stages(),
method);
}
if (method.isAnnotationPresent(After.class)) {
After annotation = method.getAnnotation(After.class);
- filterMethods.addAfterMethod(annotation.value(),
method);
+ filterMethods.addAfterMethod(annotation.stages(),
method);
}
}
}
Modified:
trunk/stripes/src/net/sourceforge/stripes/controller/DispatcherHelper.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/controller/DispatcherHelper.java
2006-10-06 02:16:47 UTC (rev 430)
+++ trunk/stripes/src/net/sourceforge/stripes/controller/DispatcherHelper.java
2006-10-07 17:50:55 UTC (rev 431)
@@ -22,6 +22,7 @@
import net.sourceforge.stripes.exception.StripesServletException;
import net.sourceforge.stripes.util.HtmlUtil;
import net.sourceforge.stripes.util.Log;
+import net.sourceforge.stripes.util.CollectionUtil;
import net.sourceforge.stripes.validation.Validatable;
import net.sourceforge.stripes.validation.ValidationError;
import net.sourceforge.stripes.validation.ValidationErrorHandler;
@@ -269,30 +270,9 @@
* @return true if the custom validation should be applied to this event,
false otherwise
*/
public static boolean applies(ValidationMethod info, String event) {
- final String[] events = info.on();
-
- if (events.length == 0 || event == null) {
- return true;
- }
- else if (events[0].startsWith("!")) {
- return !contains(events, "!" + event);
- }
- else {
- return contains(events, event);
- }
+ return CollectionUtil.applies(info.on(), event);
}
- /** A quick utility method for linear searching an array of Strings. */
- private static boolean contains(String[] arr, String target) {
- for (String item : arr) {
- if (item.equals(target)) {
- return true;
- }
- }
-
- return false;
- }
-
/**
* Finds and returns all methods in the ActionBean class and it's
superclasses that
* are marked with the ValidationMethod annotation and returns them
ordered by
Modified: trunk/stripes/src/net/sourceforge/stripes/util/CollectionUtil.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/util/CollectionUtil.java
2006-10-06 02:16:47 UTC (rev 430)
+++ trunk/stripes/src/net/sourceforge/stripes/util/CollectionUtil.java
2006-10-07 17:50:55 UTC (rev 431)
@@ -55,4 +55,26 @@
return true;
}
+
+ /**
+ * <p>Checks to see if an event is applicable given an array of event
names. The array is
+ * usually derived from the <tt>on</tt> attribute of one of the Stripes
annotations
+ * (e.g. [EMAIL PROTECTED]
net.sourceforge.stripes.validation.ValidationMethod}). The array can
+ * be composed of <i>positive</i> event names (e.g. {"foo", "bar"}) in
which case the event
+ * must be contained in the array, or negative event names (e.g.
{"!splat", "!whee"}) in
+ * which case the event must not be contained in the array.</p>
+ *
+ * <p>Calling this method with a null or zero length array will always
return true.</p>
+ *
+ * @param events an array containing event names or event names prefixed
with bangs
+ * @param event the event name to check for applicability given the array
+ * @return true if the array indicates the event is applicable, false
otherwise
+ */
+ public static boolean applies(String events[], String event) {
+ if (events == null || events.length == 0) return true;
+ boolean isPositive = events[0].charAt(0) != '!';
+
+ if (isPositive) return contains(events, event);
+ else return !contains(events, "!" + event);
+ }
}
Modified:
trunk/tests/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptorTests.java
===================================================================
---
trunk/tests/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptorTests.java
2006-10-06 02:16:47 UTC (rev 430)
+++
trunk/tests/src/net/sourceforge/stripes/controller/BeforeAfterMethodInterceptorTests.java
2006-10-07 17:50:55 UTC (rev 431)
@@ -166,6 +166,44 @@
Assert.assertEquals(actionBean.getHasCalledProtectedBeforeMethod(), 0);
}
+ @Test(groups="fast")
+ public void testIntercept_withEventSpecifier() throws Exception {
+ ExecutionContext context = new TestExecutionContext();
+ BeforeAfterMethodInterceptor interceptor = new
BeforeAfterMethodInterceptor();
+ TestActionBean2 actionBean = new TestActionBean2();
+ context.setActionBean(actionBean);
+ context.setActionBeanContext(new ActionBeanContext());
+
+ context.getActionBeanContext().setEventName("edit");
+ context.setLifecycleStage(LifecycleStage.EventHandling); // default
+ Assert.assertNotNull(interceptor.intercept(context));
+
+ context.getActionBeanContext().setEventName("save");
+ context.setLifecycleStage(LifecycleStage.EventHandling); // default
+ Assert.assertNotNull(interceptor.intercept(context));
+
+ Assert.assertEquals(actionBean.getHasCalledAfterDefaultStage(), 2);
+ Assert.assertEquals(actionBean.getHasCalledBeforeWithReturn(), 2);
+ Assert.assertEquals(actionBean.getHasCalledBeforeDefaultStage(), 2);
+ Assert.assertEquals(actionBean.getHasCalledAfterWithReturn(), 2);
+ Assert.assertEquals(actionBean.getHasCalledBeforeAfterDefaultStage(),
4);
+ Assert.assertEquals(actionBean.getHasCalledBeforeAfterOnSingleEvent(),
2);
+ Assert.assertEquals(actionBean.getHasCalledProtectedAfterMethod(), 2);
+ Assert.assertEquals(actionBean.getHasCalledProtectedBeforeMethod(), 2);
+
+ Assert.assertEquals(actionBean.getHasCalledAfterSpecificStage(), 0);
+ Assert.assertEquals(actionBean.getHasCalledAfterTwoStages(), 0);
+ Assert.assertEquals(actionBean.getHasCalledAfterWithParameter(), 0);
+
Assert.assertEquals(actionBean.getHasCalledAfterWithReturnAndParameter(), 0);
+ Assert.assertEquals(actionBean.getHasCalledBeforeSpecificStage(), 0);
+ Assert.assertEquals(actionBean.getHasCalledBeforeTwoStages(), 0);
+ Assert.assertEquals(actionBean.getHasCalledBeforeAfterSpecificStage(),
0);
+ Assert.assertEquals(actionBean.getHasCalledBeforeAfterWithParameter(),
0);
+ Assert.assertEquals(actionBean.getHasCalledBeforeWithParameter(), 0);
+
Assert.assertEquals(actionBean.getHasCalledBeforeWithReturnAndParameter(), 0);
+ Assert.assertEquals(actionBean.getHasCalledDummyMethod(), 0);
+ }
+
/**
* Test ActionBean class
* @author Jeppe Cramon
@@ -190,6 +228,7 @@
private int hasCalledBeforeAfterWithParameter;
private int hasCalledBeforeAfterSpecificStage;
private int hasCalledBeforeAfterDefaultStage;
+ private int hasCalledBeforeAfterOnSingleEvent;
public void setContext(ActionBeanContext context) {
}
@@ -198,7 +237,7 @@
return null;
}
- @Before(LifecycleStage.ActionBeanResolution)
+ @Before(stages=LifecycleStage.ActionBeanResolution)
public void beforeActionBeanResolutionWillNeverBeCalled() {
hasCalledBeforeActionBeanResolutionWillNeverBeCalled++;
}
@@ -208,12 +247,12 @@
hasCalledBeforeDefaultStage++;
}
- @Before(LifecycleStage.HandlerResolution)
+ @Before(stages=LifecycleStage.HandlerResolution)
public void beforeSpecificStage() {
hasCalledBeforeSpecificStage++;
}
- @Before({LifecycleStage.BindingAndValidation,
LifecycleStage.CustomValidation})
+ @Before(stages={LifecycleStage.BindingAndValidation,
LifecycleStage.CustomValidation})
public void beforeTwoStages() {
hasCalledBeforeTwoStages++;
}
@@ -237,7 +276,7 @@
return null;
}
- /** Intercept methods must be public. */
+ /** Should work just like a public method. */
@Before
protected void protectedBeforeMethod() {
hasCalledProtectedBeforeMethod++;
@@ -253,12 +292,12 @@
hasCalledAfterDefaultStage++;
}
- @After(LifecycleStage.ActionBeanResolution)
+ @After(stages=LifecycleStage.ActionBeanResolution)
public void afterSpecificStage() {
hasCalledAfterSpecificStage++;
}
- @After({LifecycleStage.HandlerResolution,
LifecycleStage.CustomValidation})
+ @After(stages={LifecycleStage.HandlerResolution,
LifecycleStage.CustomValidation})
public void afterTwoStages() {
hasCalledAfterTwoStages++;
}
@@ -283,7 +322,7 @@
return null;
}
- /** Not included because methods must be public. */
+ /** Should work just like a public method. */
@After
protected void protectedAfterMethod() {
hasCalledProtectedAfterMethod++;
@@ -298,8 +337,8 @@
}
/** Invoked only at those stages listed. */
- @Before(LifecycleStage.BindingAndValidation)
- @After(LifecycleStage.CustomValidation)
+ @Before(stages=LifecycleStage.BindingAndValidation)
+ @After(stages=LifecycleStage.CustomValidation)
public void beforeAfterSpecificStage() {
hasCalledBeforeAfterSpecificStage++;
}
@@ -310,6 +349,12 @@
hasCalledBeforeAfterDefaultStage++;
}
+ /** Invoked only at default EventHandling stage. */
+ @Before(on="edit") @After(on="save")
+ public void beforeAfterOnSingleEvent() {
+ hasCalledBeforeAfterOnSingleEvent++;
+ }
+
// -- Unit test properties --
public int getHasCalledAfterDefaultStage() { return
hasCalledAfterDefaultStage; }
public int getHasCalledAfterSpecificStage() { return
hasCalledAfterSpecificStage; }
@@ -330,6 +375,7 @@
public int getHasCalledProtectedAfterMethod() { return
hasCalledProtectedAfterMethod; }
public int getHasCalledProtectedBeforeMethod() { return
hasCalledProtectedBeforeMethod; }
public int getHasCalledBeforeAfterDefaultStage() { return
hasCalledBeforeAfterDefaultStage; }
+ public int getHasCalledBeforeAfterOnSingleEvent() { return
hasCalledBeforeAfterOnSingleEvent; }
}
private static class TestExecutionContext extends ExecutionContext {
@@ -337,7 +383,5 @@
public Resolution proceed() throws Exception {
return new ForwardResolution("wakker");
}
-
}
-
}
Modified:
trunk/tests/src/net/sourceforge/stripes/controller/MapBindingTests.java
===================================================================
--- trunk/tests/src/net/sourceforge/stripes/controller/MapBindingTests.java
2006-10-06 02:16:47 UTC (rev 430)
+++ trunk/tests/src/net/sourceforge/stripes/controller/MapBindingTests.java
2006-10-07 17:50:55 UTC (rev 431)
@@ -239,7 +239,7 @@
Assert.assertNotNull(bean.getMapDateDate().get(key));
}
- @Before(LifecycleStage.BindingAndValidation)
+ @Before(stages=LifecycleStage.BindingAndValidation)
public void populateTypelessMap() {
this.typelessMap = new HashMap();
this.typelessMap.put(1, new TestBean());
Modified: trunk/tests/src/net/sourceforge/stripes/util/CollectionUtilTest.java
===================================================================
--- trunk/tests/src/net/sourceforge/stripes/util/CollectionUtilTest.java
2006-10-06 02:16:47 UTC (rev 430)
+++ trunk/tests/src/net/sourceforge/stripes/util/CollectionUtilTest.java
2006-10-07 17:50:55 UTC (rev 431)
@@ -43,4 +43,14 @@
public void testEmptyOnNonEmptyCollection3() {
Assert.assertFalse(CollectionUtil.empty(new String[] {"bar", "splat",
"foo"}));
}
+
+ @Test(groups="fast")
+ public void testApplies() {
+ Assert.assertTrue(CollectionUtil.applies(null, "foo"));
+ Assert.assertTrue(CollectionUtil.applies(new String[] {}, "foo"));
+ Assert.assertTrue(CollectionUtil.applies(new String[] {"bar", "foo"},
"foo"));
+ Assert.assertFalse(CollectionUtil.applies(new String[] {"bar", "f00"},
"foo"));
+ Assert.assertFalse(CollectionUtil.applies(new String[] {"!bar",
"!foo"}, "foo"));
+ Assert.assertTrue(CollectionUtil.applies(new String[] {"!bar",
"!f00"}, "foo"));
+ }
}
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development