001: /*
002: * Created on 7 Aug 2006
003: */
004: package uk.org.ponder.springutil;
005:
006: import java.util.ArrayList;
007: import java.util.HashMap;
008: import java.util.Iterator;
009: import java.util.List;
010: import java.util.Map;
011: import java.util.Set;
012:
013: import org.springframework.beans.BeansException;
014: import org.springframework.beans.factory.config.BeanPostProcessor;
015: import org.springframework.context.ApplicationContext;
016: import org.springframework.context.ApplicationContextAware;
017:
018: import uk.org.ponder.arrayutil.MapUtil;
019: import uk.org.ponder.beanutil.BeanLocator;
020: import uk.org.ponder.rsac.RSACBeanLocator;
021: import uk.org.ponder.saxalizer.AccessMethod;
022: import uk.org.ponder.saxalizer.MethodAnalyser;
023: import uk.org.ponder.saxalizer.SAXalizerMappingContext;
024: import uk.org.ponder.util.Logger;
025:
026: /**
027: * Does the work of collecting and focusing all the distributed property
028: * deliveries onto the target list-valued bean property.
029: *
030: * @author Antranig Basman (antranig@caret.cam.ac.uk)
031: *
032: */
033:
034: public class TLABPostProcessor implements BeanPostProcessor,
035: ApplicationContextAware {
036:
037: private Map targetMap = new HashMap();
038:
039: private SAXalizerMappingContext mappingContext;
040: private ApplicationContext applicationContext;
041:
042: // If this is set, will be used in preference to applicationContext to resolve
043: // beans
044: private RSACBeanLocator rsacbeanlocator;
045:
046: public void setMappingContext(SAXalizerMappingContext mappingContext) {
047: this .mappingContext = mappingContext;
048: }
049:
050: public void init() {
051: if (mappingContext == null) {
052: mappingContext = SAXalizerMappingContext.instance();
053: }
054: }
055:
056: // Intended for fast request-scope deployment
057: public TLABPostProcessor copy() {
058: TLABPostProcessor togo = new TLABPostProcessor();
059: togo.targetMap = targetMap;
060: togo.mappingContext = mappingContext;
061: return togo;
062: }
063:
064: // VERY temporary method just to get support for bindbefore/bindafter="*"
065: private void sortTLABs(List tlabs) {
066: int limit = tlabs.size();
067: for (int i = 0; i < limit; ++i) {
068: TargetListAggregatingBean tlab = (TargetListAggregatingBean) tlabs
069: .get(i);
070: Object bindafter = tlab.getBindAfter();
071: if ("*".equals(bindafter)) {
072: tlabs.remove(i);
073: tlabs.add(tlab);
074: --i;
075: --limit;
076: }
077: Object bindbefore = tlab.getBindBefore();
078: if ("*".equals(bindbefore)) {
079: tlabs.remove(i);
080: tlabs.add(0, tlab);
081: }
082: }
083: }
084:
085: public void setApplicationContext(
086: ApplicationContext applicationContext) {
087: this .applicationContext = applicationContext;
088: // We do this here so that fewer will have to come after us!
089: String[] viewbeans = applicationContext.getBeanNamesForType(
090: TargetListAggregatingBean.class, false, false);
091: for (int i = 0; i < viewbeans.length; ++i) {
092: String viewbean = viewbeans[i];
093: TargetListAggregatingBean tlab = (TargetListAggregatingBean) applicationContext
094: .getBean(viewbean);
095: validateTLAB(tlab, viewbean);
096:
097: MapUtil.putMultiMap(targetMap, tlab.getTargetBean(), tlab);
098: }
099:
100: for (Iterator values = targetMap.values().iterator(); values
101: .hasNext();) {
102: List tlabs = (List) values.next();
103: sortTLABs(tlabs);
104: }
105: }
106:
107: private void validateTLAB(TargetListAggregatingBean tlab,
108: String viewbean) {
109: int getters = 0;
110: if (tlab.getValue() != null)
111: ++getters;
112: if (tlab.getValueRef() != null)
113: ++getters;
114: if (tlab.getValueRefs() != null)
115: ++getters;
116: if (getters != 1) {
117: throw new IllegalArgumentException(
118: "Error reading TargetListAggregatingBean "
119: + viewbean
120: + ": exactly one of value or valueRef must be set");
121: }
122: }
123:
124: private Object fetchValue(TargetListAggregatingBean tlab,
125: BeanLocator fetcher) {
126: Object value = tlab.getValue();
127: if (value == null) {
128: if (tlab.getValueRef() != null) {
129: value = fetcher.locateBean(tlab.getValueRef());
130: } else {
131: List list = new ArrayList();
132: String[] beans = tlab.getValueRefs();
133: for (int i = 0; i < beans.length; ++i) {
134: list.add(fetcher.locateBean(beans[i]));
135: }
136: value = list;
137: }
138: }
139: return value;
140: }
141:
142: public void checkGuard(Set loaded) {
143: int cloaded = 0;
144: for (Iterator keyit = targetMap.keySet().iterator(); keyit
145: .hasNext();) {
146: String key = (String) keyit.next();
147: if (loaded.contains(key)) {
148: Logger.log
149: .error("Bean "
150: + key
151: + " which was the target of TLAB definition has already been "
152: + "loaded by search for TLAB definitions during startup");
153: ++cloaded;
154: }
155: }
156: if (cloaded > 0) {
157: throw new IllegalArgumentException(
158: cloaded
159: + (cloaded > 1 ? " beans which were"
160: : " bean which was")
161: + " the target of TLAB definition became loaded"
162: + " by search for TLAB definitions during startup - consider breaking"
163: + " this cycle by use of valueRef rather than value in the TLAB definition");
164: }
165: }
166:
167: public void setRSACBeanLocator(RSACBeanLocator rsacbeanlocator) {
168: this .rsacbeanlocator = rsacbeanlocator;
169: }
170:
171: public Object postProcessAfterInitialization(Object bean,
172: String beanName) {
173: return bean;
174: }
175:
176: public Object postProcessBeforeInitialization(Object bean,
177: String beanName) throws BeansException {
178: // Perhaps in Ruby, Perl, or Haskell, this method body is just 4 lines!
179:
180: BeanLocator fetcher = rsacbeanlocator == null ? new BeanFactoryBeanLocator(
181: applicationContext)
182: : (BeanLocator) rsacbeanlocator.getBeanLocator();
183: List tlabs = (List) targetMap.get(beanName);
184: if (tlabs == null)
185: return bean;
186: Map listprops = new HashMap(); // map of property name to list
187: for (int i = 0; i < tlabs.size(); ++i) {
188: TargetListAggregatingBean tlab = (TargetListAggregatingBean) tlabs
189: .get(i);
190: Object value = fetchValue(tlab, fetcher);
191: if (tlab.getUnwrapLists() && value instanceof List) {
192: List values = (List) value;
193: for (int j = 0; j < values.size(); ++j) {
194: MapUtil.putMultiMap(listprops, tlab
195: .getTargetProperty(), values.get(j));
196: }
197: } else {
198: MapUtil.putMultiMap(listprops,
199: tlab.getTargetProperty(), value);
200: }
201: }
202: for (Iterator propit = listprops.keySet().iterator(); propit
203: .hasNext();) {
204: String propname = (String) propit.next();
205: Object value = listprops.get(propname);
206: MethodAnalyser ma = mappingContext.getAnalyser(bean
207: .getClass());
208: AccessMethod sam = ma.getAccessMethod(propname);
209: if (sam == null || !sam.canSet()) {
210: throw new IllegalArgumentException(
211: "TLAB target bean "
212: + beanName
213: + " does not have any writeable property named "
214: + propname);
215: }
216: sam.setChildObject(bean, value);
217: }
218: return bean;
219: }
220:
221: }
|