001: /*
002: * Created on Nov 16, 2005
003: */
004: package uk.org.ponder.rsf.preservation;
005:
006: import java.util.HashMap;
007: import java.util.HashSet;
008: import java.util.Iterator;
009: import java.util.Map;
010: import java.util.Set;
011:
012: import org.springframework.beans.factory.BeanNameAware;
013:
014: import uk.org.ponder.beanutil.BeanLocator;
015: import uk.org.ponder.beanutil.BeanModelAlterer;
016: import uk.org.ponder.beanutil.IterableBeanLocator;
017: import uk.org.ponder.beanutil.PathUtil;
018: import uk.org.ponder.beanutil.WriteableBeanLocator;
019: import uk.org.ponder.errorutil.ThreadErrorState;
020: import uk.org.ponder.messageutil.TargettedMessageList;
021: import uk.org.ponder.rsf.state.ExpiredFlowException;
022: import uk.org.ponder.rsf.state.TokenStateHolder;
023: import uk.org.ponder.stringutil.StringList;
024: import uk.org.ponder.util.Logger;
025: import uk.org.ponder.util.UniversalRuntimeException;
026:
027: /**
028: * A preservation strategy that works by simple copying of bean object
029: * references. These should probably be "Serializable" if their destination is
030: * a Session.
031: * @author Antranig Basman (antranig@caret.cam.ac.uk)
032: *
033: */
034: // * If the target beans are serializable, then so will be the entry sent to the
035: // * tokenstateholder. If so this state may be passed to a
036: // * ClientFormTokenStateHolder or other similar - for Hibernate-style
037: // * non-serializable beans, they must be sent to an InMemoryTSH. But on the other
038: // * hand, these beans will need a Session.lock() called on them in any case, so
039: // * need to derive from the BCPS for a HibernateBCPS.
040: public class BeanCopyPreservationStrategy implements
041: TSHPreservationStrategy, BeanNameAware,
042: GenericBeanCopyPreservationStrategy {
043: private StringList beannames;
044: private TokenStateHolder holder;
045: private String basekey = "";
046: private BeanModelAlterer alterer;
047: private boolean expected = true;
048: private boolean classLoaderInternal = true;
049:
050: private Set ourLoaders;
051:
052: public void setClassLoaderInternal(boolean classLoaderInternal) {
053: this .classLoaderInternal = classLoaderInternal;
054: }
055:
056: public BeanCopyPreservationStrategy() {
057: ourLoaders = new HashSet();
058: ClassLoader ours = getClass().getClassLoader();
059: while (ours != null) {
060: ourLoaders.add(ours);
061: ours = ours.getParent();
062: }
063: }
064:
065: public void setPreservingBeans(StringList beannames) {
066: this .beannames = beannames;
067: }
068:
069: public void setTokenStateHolder(TokenStateHolder holder) {
070: this .holder = holder;
071: }
072:
073: public TokenStateHolder getTokenStateHolder() {
074: return holder;
075: }
076:
077: public void setBeanModelAlterer(BeanModelAlterer alterer) {
078: this .alterer = alterer;
079: }
080:
081: public void setStorageExpected(boolean expected) {
082: this .expected = expected;
083: }
084:
085: public void setBaseKey(String basekey) {
086: this .basekey = basekey;
087: }
088:
089: public int preserveImmediate(BeanLocator source,
090: StringList beannames, String tokenid) {
091: HashMap beans = new HashMap();
092: for (int i = 0; i < beannames.size(); ++i) {
093: String beanname = beannames.stringAt(i);
094: Object bean = alterer.getBeanValue(beanname, source, null);
095: // This branch generally useful for entity bean locators
096: if (bean instanceof IterableBeanLocator) {
097: IterableBeanLocator iterablebean = (IterableBeanLocator) bean;
098: for (Iterator beanit = iterablebean.iterator(); beanit
099: .hasNext();) {
100: String subbeanname = (String) beanit.next();
101: Object subbean = iterablebean
102: .locateBean(subbeanname);
103: String fullpath = PathUtil.buildPath(beanname,
104: subbeanname);
105: beans.put(fullpath, subbean);
106: }
107: }
108: if (bean != null) {
109: beans.put(beanname, bean);
110: Logger.log.info("BeanCopy preserved to path "
111: + beanname + ": " + bean);
112: }
113: }
114: String token = basekey + tokenid;
115: holder.putTokenState(token, beans);
116: Logger.log.info("BeanCopy saved " + beans.size()
117: + " beans to token " + token);
118: return beans.size();
119: }
120:
121: public int preserve(BeanLocator source, String tokenid) {
122: return preserveImmediate(source, beannames, tokenid);
123: }
124:
125: public int restore(WriteableBeanLocator target, String tokenid) {
126: String token = basekey + tokenid;
127: Logger.log.info("BeanCopy looking for state token " + token);
128: Map beans = (Map) holder.getTokenState(token);
129: if (beans == null) {
130: if (expected) {
131: throw UniversalRuntimeException.accumulate(
132: new ExpiredFlowException(),
133: "Client requested restoration of expired flow state with IDDD "
134: + tokenid);
135: }
136: return 0;
137: } else {
138: for (Iterator keyit = beans.keySet().iterator(); keyit
139: .hasNext();) {
140: String beanname = (String) keyit.next();
141: Object bean = beans.get(beanname);
142: ClassLoader beanloader = bean.getClass()
143: .getClassLoader();
144: if (classLoaderInternal
145: && !ourLoaders.contains(beanloader)) {
146: Logger.log.warn("Bean " + bean + " with name "
147: + beanname
148: + " from unrecognized ClassLoader "
149: + beanloader.getClass().getName() + "@"
150: + System.identityHashCode(beanloader)
151: + " was destroyed from preservation");
152: beans.remove(beanname);
153: } else {
154: TargettedMessageList messages = ThreadErrorState
155: .getErrorState().messages;
156: // Logger.log.info("Restoring bean " + bean + " to path " + beanname);
157: alterer.setBeanValue(beanname, target, bean,
158: messages, false);
159: }
160: }
161: return beans.size();
162: }
163: }
164:
165: public void clear(String tokenid) {
166: String token = basekey + tokenid;
167: Logger.log.info("Cleared token state for " + token);
168: holder.clearTokenState(token);
169: }
170:
171: public void setBeanName(String name) {
172: this.basekey = name;
173: }
174:
175: }
|