001: /*
002: * Created on 25 Jul 2006
003: */
004: package uk.org.ponder.rsf.state.guards;
005:
006: import java.util.ArrayList;
007: import java.util.List;
008:
009: import org.springframework.context.ApplicationContext;
010: import org.springframework.context.ApplicationContextAware;
011: import org.springframework.validation.BindException;
012: import org.springframework.validation.Validator;
013:
014: import uk.org.ponder.beanutil.BeanLocator;
015: import uk.org.ponder.beanutil.BeanModelAlterer;
016: import uk.org.ponder.beanutil.PathUtil;
017: import uk.org.ponder.mapping.BeanInvalidationModel;
018: import uk.org.ponder.messageutil.TargettedMessage;
019: import uk.org.ponder.messageutil.TargettedMessageList;
020: import uk.org.ponder.springutil.errors.SpringErrorConverter;
021: import uk.org.ponder.util.CollectingRunnableInvoker;
022: import uk.org.ponder.util.RunnableInvoker;
023:
024: /**
025: * Collects all BeanGuard definitions from the context, and supervises their
026: * application.
027: *
028: * @author Antranig Basman (antranig@caret.cam.ac.uk)
029: */
030:
031: // TODO: This implementation currently scales very badly - cost of every
032: // bean model write is proportional to number of guards.
033: public class BeanGuardProcessor implements ApplicationContextAware {
034:
035: private BeanGuard[] guards;
036: private BeanModelAlterer darapplier;
037:
038: public void setApplicationContext(
039: ApplicationContext applicationContext) {
040: String[] guardnames = applicationContext.getBeanNamesForType(
041: BeanGuard.class, false, false);
042: guards = new BeanGuard[guardnames.length];
043: for (int i = 0; i < guardnames.length; ++i) {
044: guards[i] = (BeanGuard) applicationContext
045: .getBean(guardnames[i]);
046: }
047: }
048:
049: public void setBeanModelAlterer(BeanModelAlterer darapplier) {
050: this .darapplier = darapplier;
051: }
052:
053: public RunnableInvoker getGuardProcessor(
054: final BeanInvalidationModel bim,
055: final TargettedMessageList errors, final BeanLocator rbl) {
056: return new RunnableInvoker() {
057: public void invokeRunnable(Runnable torun) {
058: processGuards(bim, errors, rbl, torun);
059: }
060: };
061: }
062:
063: public void processPostGuards(BeanInvalidationModel bim,
064: TargettedMessageList errors, BeanLocator rbl) {
065: processGuards(bim, errors, rbl, null);
066: }
067:
068: private List appendWrapper(List toappend, RunnableInvoker invoker) {
069: if (toappend == null) {
070: toappend = new ArrayList();
071: }
072: toappend.add(invoker);
073: return toappend;
074: }
075:
076: private void processGuards(BeanInvalidationModel bim,
077: TargettedMessageList errors, BeanLocator rbl,
078: Runnable toinvoke) {
079: BindException springerrors = null;
080: List wrappers = null;
081: for (int i = 0; i < guards.length; ++i) {
082: BeanGuard guarddef = guards[i];
083: String mode = guarddef.getGuardMode();
084: String timing = guarddef.getGuardTiming();
085: String guardedpath = guarddef.getGuardedPath();
086: String guardmethod = guarddef.getGuardMethod();
087: String guardEL = guarddef.getGuardEL();
088: String guardproperty = guarddef.getGuardProperty();
089: if (guardEL != null && guardmethod != null) {
090: guardmethod = PathUtil
091: .composePath(guardEL, guardmethod);
092: }
093: if (mode.equals(BeanGuard.WRITE) && timing == null
094: || timing.equals(BeanGuard.POST)) {
095: // for each POST-WRITE guard for an invalidated path, execute it.
096: String match = bim.invalidPathMatch(guardedpath);
097: if (match != null) {
098: Object guard = guarddef.getGuard();
099: if (guard == null && guardEL == null) {
100: if (guardmethod != null) {
101: guardEL = PathUtil
102: .getToTailPath(guardmethod);
103: guardmethod = PathUtil
104: .getTailPath(guardmethod);
105: } else if (guardproperty != null) {
106: guardEL = PathUtil
107: .getToTailPath(guardproperty);
108: guardproperty = PathUtil
109: .getTailPath(guardproperty);
110: }
111: }
112: if (guardEL != null) {
113: guard = darapplier.getBeanValue(guardEL, rbl,
114: null);
115: }
116: Object guarded = darapplier.getBeanValue(match,
117: rbl, null);
118: try {
119: if (guard instanceof RunnableInvoker) {
120: if (toinvoke == null) {
121: throw new IllegalArgumentException(
122: "Configuration error: Bean Guard "
123: + guard
124: + " at "
125: + guardEL
126: + " was required in AROUND mode but does not implement RunnableInvoker");
127: } else {
128: wrappers = appendWrapper(wrappers,
129: (RunnableInvoker) guard);
130: }
131: } else {
132: // now invoking postguards
133: if (toinvoke == null) {
134: if (guard instanceof Validator) {
135: if (guarded == null) {
136: throw new IllegalArgumentException(
137: "Error: Spring Validator may not be used to validate a null object");
138: }
139: Validator guardv = (Validator) guard;
140: // NB, a Spring validator may not be applied to a null object!
141: springerrors = new BindException(
142: guarded, guardedpath);
143: guardv.validate(guarded,
144: springerrors);
145: SpringErrorConverter.appendErrors(
146: guardedpath, errors,
147: springerrors);
148: } else {
149: errors
150: .pushNestedPath(guardedpath
151: + TargettedMessageList.BACKUP_PATH);
152: try {
153: if (guardmethod != null) {
154: darapplier
155: .invokeBeanMethod(
156: guardmethod,
157: guard);
158: } else if (guardproperty != null) {
159: darapplier.setBeanValue(
160: guardproperty,
161: guard, guarded,
162: errors, false);
163: }
164: } finally {
165: errors.popNestedPath();
166: }
167: }
168: }
169: }
170: } catch (Exception e) {
171: TargettedMessage message = new TargettedMessage(
172: e.getMessage(), e, match);
173: errors.addMessage(message);
174: }
175: }
176: }
177: }
178: if (toinvoke != null) {
179: CollectingRunnableInvoker
180: .invokeWrappers(wrappers, toinvoke);
181: }
182: // if (springerrors != null) {
183: // throw UniversalRuntimeException.accumulate(springerrors);
184: // }
185: }
186: }
|