001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.aspects.versioned;
023:
024: import org.jboss.aop.Advised;
025: import org.jboss.aop.ClassAdvisor;
026: import org.jboss.aop.InstanceAdvised;
027: import org.jboss.logging.Logger;
028: import org.jboss.tm.TransactionLocal;
029: import org.jboss.util.id.GUID;
030:
031: import javax.naming.InitialContext;
032: import javax.transaction.Transaction;
033: import javax.transaction.TransactionManager;
034:
035: import java.lang.ref.WeakReference;
036: import java.lang.reflect.Field;
037: import java.util.HashMap;
038: import java.util.Iterator;
039: import java.util.Map;
040:
041: /**
042: *
043: * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
044: * @version $Revision: 57186 $
045: */
046: public class DistributedPOJOState extends StateManager implements
047: DistributedState, java.io.Externalizable {
048: private static final long serialVersionUID = 7640633352012924284L;
049:
050: /**
051: * Logging instance
052: */
053: private static Logger log = Logger
054: .getLogger(DistributedPOJOState.class);
055:
056: protected String classname;
057: protected HashMap fieldMap;
058: transient protected TransactionManager tm;
059: transient protected WeakReference advisedRef;
060: transient protected TransactionLocal txState = new TransactionLocal();
061: transient protected SynchronizationManager synchManager;
062: transient protected DistributedVersionManager versionManager;
063:
064: public DistributedPOJOState() {
065: }
066:
067: public DistributedPOJOState(GUID daguid, long datimeout,
068: Advised advised, DistributedVersionManager versionManager,
069: SynchronizationManager synchManager) throws Exception {
070: super (daguid, datimeout);
071: this .fieldMap = new HashMap();
072: this .classname = advised.getClass().getName();
073: InitialContext ctx = new InitialContext();
074: this .tm = (TransactionManager) ctx
075: .lookup("java:/TransactionManager");
076: this .synchManager = synchManager;
077: this .versionManager = versionManager;
078: this .advisedRef = new WeakReference(advised);
079: }
080:
081: public InstanceAdvised getObject() {
082: if (advisedRef != null) {
083: return (InstanceAdvised) advisedRef.get();
084: }
085: return null;
086: }
087:
088: public boolean equals(Object obj) {
089: if (!(obj instanceof DistributedPOJOState))
090: return false;
091: DistributedPOJOState pojo = (DistributedPOJOState) obj;
092: return guid.equals(pojo.guid);
093: }
094:
095: public int hashCode() {
096: return guid.hashCode();
097:
098: }
099:
100: public InstanceAdvised buildObject(SynchronizationManager manager,
101: DistributedVersionManager versionManager) throws Exception {
102: log.trace("building a " + classname + " of guid " + guid);
103: this .versionManager = versionManager;
104: this .synchManager = manager;
105: Class clazz = Thread.currentThread().getContextClassLoader()
106: .loadClass(classname);
107: Advised advised = (Advised) clazz.newInstance();
108: this .advisedRef = new WeakReference(advised);
109: versionManager.addVersioning(this , advised);
110: manager.putState(guid, this );
111: manager.putObject(guid, advised);
112:
113: Iterator it = fieldMap.values().iterator();
114: while (it.hasNext()) {
115: DistributedFieldUpdate update = (DistributedFieldUpdate) it
116: .next();
117: ClassAdvisor advisor = (ClassAdvisor) advised._getAdvisor();
118: log
119: .trace("build field "
120: + advisor.getAdvisedFields()[update
121: .getFieldIndex()].getName());
122: Object val = update.getNonDereferencedValue();
123: if (val != null && (val instanceof VersionReference)) {
124: VersionReference ref = (VersionReference) val;
125: log.trace("VersionReference.guid: "
126: + ref.getGUID()
127: + " for field "
128: + advisor.getAdvisedFields()[update
129: .getFieldIndex()].getName());
130: val = manager.getObject(ref.getGUID());
131: if (val == null) {
132: DistributedState fieldVal = manager.getState(ref
133: .getGUID());
134: val = fieldVal.buildObject(manager, versionManager);
135: }
136: ref.set((InstanceAdvised) val);
137: }
138: }
139: return advised;
140: }
141:
142: public HashMap getTxState() {
143: return (HashMap) txState.get();
144: }
145:
146: public HashMap getTxState(Transaction tx) {
147: return (HashMap) txState.get(tx);
148: }
149:
150: public Object fieldRead(
151: org.jboss.aop.joinpoint.Invocation invocation)
152: throws Throwable {
153: acquireReadLock();
154: try {
155: org.jboss.aop.joinpoint.FieldReadInvocation fieldInvocation = (org.jboss.aop.joinpoint.FieldReadInvocation) invocation;
156: Integer index = new Integer(fieldInvocation.getIndex());
157: HashMap map = getTxState();
158: if (map == null) {
159: map = fieldMap;
160: }
161: DistributedFieldUpdate update = (DistributedFieldUpdate) map
162: .get(index);
163: Object val = update.getValue();
164: return val;
165: } finally {
166: releaseReadLock();
167: }
168: }
169:
170: public Object fieldWrite(
171: org.jboss.aop.joinpoint.Invocation invocation)
172: throws Throwable {
173: org.jboss.aop.joinpoint.FieldWriteInvocation fieldInvocation = (org.jboss.aop.joinpoint.FieldWriteInvocation) invocation;
174: Integer index = new Integer(fieldInvocation.getIndex());
175: Object val = fieldInvocation.getValue();
176:
177: if (val instanceof Advised) {
178: Advised advisedValue = (Advised) val;
179: val = versionManager.makeVersioned(advisedValue);
180: }
181:
182: Transaction tx = tm.getTransaction();
183: if (tx == null) {
184: acquireWriteLock();
185: try {
186: // REVISIT: Handle exception
187: DistributedFieldUpdate update = (DistributedFieldUpdate) fieldMap
188: .get(index);
189: long versionId = update.getVersionId() + 1;
190: update.setVersionId(versionId);
191: update.setValue(val);
192: HashMap fieldUpdates = new HashMap();
193: fieldUpdates.put(index, update);
194: synchManager.noTxUpdate(new DistributedPOJOUpdate(guid,
195: fieldUpdates));
196: return null;
197: } finally {
198: releaseWriteLock();
199: }
200: }
201:
202: acquireReadLock();
203: try {
204: HashMap map = (HashMap) txState.get();
205: if (map == null) {
206: map = new HashMap();
207: DistributedFieldUpdate update = (DistributedFieldUpdate) fieldMap
208: .get(index);
209: DistributedFieldUpdate newUpdate = new DistributedFieldUpdate(
210: val, update.getVersionId() + 1, index
211: .intValue());
212: synchManager.registerUpdate(tx, this );
213: map.put(index, newUpdate);
214: txState.set(tx, map);
215: } else {
216: DistributedFieldUpdate newUpdate = (DistributedFieldUpdate) map
217: .get(index);
218: if (newUpdate == null) {
219: DistributedFieldUpdate update = (DistributedFieldUpdate) fieldMap
220: .get(index);
221: newUpdate = new DistributedFieldUpdate(val, update
222: .getVersionId() + 1, index.intValue());
223: map.put(index, newUpdate);
224: } else {
225: newUpdate.setValue(val);
226: }
227: }
228: } finally {
229: releaseReadLock();
230: }
231:
232: return null;
233: }
234:
235: public DistributedUpdate createTxUpdate(Transaction tx) {
236: HashMap state = getTxState(tx);
237: return new DistributedPOJOUpdate(guid, state);
238: }
239:
240: public void checkOptimisticLock(Transaction tx) {
241: // NOTE THIS CODE ASSUMES THAT A WRITELOCK HAS BEEN ACQUIRED!!!!
242: HashMap state = getTxState(tx);
243: Iterator it = state.entrySet().iterator();
244: while (it.hasNext()) {
245: Map.Entry entry = (Map.Entry) it.next();
246: Integer index = (Integer) entry.getKey();
247: DistributedFieldUpdate update = (DistributedFieldUpdate) entry
248: .getValue();
249: DistributedFieldUpdate orig = (DistributedFieldUpdate) fieldMap
250: .get(index);
251: if (update.getVersionId() <= orig.getVersionId()) {
252: Advised advised = null;
253: if (advisedRef != null) {
254: advised = (Advised) advisedRef.get();
255: }
256: if (advised != null) {
257: ClassAdvisor advisor = (ClassAdvisor) advised
258: ._getAdvisor();
259: Field field = advisor.getAdvisedFields()[index
260: .intValue()];
261: throw new OptimisticLockFailure(
262: "optimistic lock failure for field "
263: + field.getName()
264: + " of class "
265: + field.getDeclaringClass()
266: .getName());
267: }
268: }
269: }
270: }
271:
272: public void mergeState(Transaction tx) throws Exception {
273: HashMap newState = getTxState(tx);
274: mergeState(newState);
275: }
276:
277: public void mergeState(DistributedUpdate update) throws Exception {
278: HashMap newState = ((DistributedPOJOUpdate) update).fieldUpdates;
279: mergeState(newState);
280: }
281:
282: public void mergeState(HashMap newState) throws Exception {
283: // NOTE THIS CODE ASSUMES THAT A WRITELOCK HAS BEEN ACQUIRED!!!!
284: Iterator it = newState.entrySet().iterator();
285: while (it.hasNext()) {
286: Map.Entry entry = (Map.Entry) it.next();
287: DistributedFieldUpdate update = (DistributedFieldUpdate) entry
288: .getValue();
289: if (update.getNonDereferencedValue() instanceof VersionReference) {
290: VersionReference ref = (VersionReference) update
291: .getNonDereferencedValue();
292: if (ref.get() == null)
293: ref.set((InstanceAdvised) synchManager
294: .getObject(ref.getGUID()));
295: }
296: }
297: fieldMap.putAll(newState); // overwrite old state
298: }
299:
300: public void writeExternal(java.io.ObjectOutput out)
301: throws java.io.IOException {
302: super .writeExternal(out);
303: out.writeObject(classname);
304: out.writeObject(fieldMap);
305: }
306:
307: public void readExternal(java.io.ObjectInput in)
308: throws java.io.IOException, ClassNotFoundException {
309: super .readExternal(in);
310: this .classname = (String) in.readObject();
311: this .fieldMap = (HashMap) in.readObject();
312: try {
313: InitialContext ctx = new InitialContext();
314: this .tm = (TransactionManager) ctx
315: .lookup("java:/TransactionManager");
316: } catch (Exception ex) {
317: throw new RuntimeException(ex);
318: }
319: this .txState = new TransactionLocal();
320: }
321:
322: }
|