001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.objectserver.managedobject;
005:
006: import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
007:
008: import com.tc.exception.TCRuntimeException;
009: import com.tc.io.serializer.TCObjectInputStream;
010: import com.tc.io.serializer.TCObjectOutputStream;
011: import com.tc.object.ObjectID;
012: import com.tc.object.dna.api.DNACursor;
013: import com.tc.object.dna.api.PhysicalAction;
014: import com.tc.objectserver.managedobject.bytecode.ClassSpec;
015: import com.tc.objectserver.managedobject.bytecode.FieldType;
016: import com.tc.objectserver.managedobject.bytecode.PhysicalStateClassLoader;
017: import com.tc.objectserver.persistence.api.ClassPersistor;
018: import com.tc.util.Assert;
019:
020: import java.io.ByteArrayInputStream;
021: import java.io.ByteArrayOutputStream;
022: import java.lang.reflect.Constructor;
023: import java.util.ArrayList;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Map;
028: import java.util.SortedMap;
029: import java.util.TreeMap;
030: import java.util.Map.Entry;
031:
032: public class PhysicalManagedObjectStateFactory {
033:
034: private static final Class[] CONSTRUCTOR_PARAMS_CLASS = new Class[0];
035: private static final Object[] CONSTRUCTOR_PARAMS = new Object[0];
036: private final PhysicalStateClassLoader loader;
037: private final Map knownClasses;
038: private final ClassPersistor persistor;
039: private int sequenceId = 0;
040:
041: public PhysicalManagedObjectStateFactory(ClassPersistor persistor) {
042: this .loader = new PhysicalStateClassLoader();
043: this .knownClasses = new ConcurrentHashMap();
044: this .persistor = persistor;
045: loadAllClassesFromDB();
046: }
047:
048: private synchronized void loadAllClassesFromDB() {
049: SortedMap map = new TreeMap(persistor.retrieveAllClasses());
050: for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
051: Map.Entry e = (Entry) i.next();
052: Integer clazzId = (Integer) e.getKey();
053: byte clazzBytes[] = (byte[]) e.getValue();
054: int cid = clazzId.intValue();
055: if (sequenceId < cid) {
056: sequenceId = cid;
057: }
058: loadFromBytes(cid, clazzBytes);
059: }
060: }
061:
062: private void addKnownClasses(String classIdentifier,
063: String genClassName, int cid) {
064: knownClasses.put(classIdentifier, genClassName);
065: knownClasses.put(new Integer(cid), genClassName);
066: }
067:
068: private void addKnownClasses(ClassSpec cs) {
069: addKnownClasses(cs.getClassIdentifier(), cs
070: .getGeneratedClassName(), cs.getClassID());
071: }
072:
073: private void loadFromBytes(int classId, byte[] clazzBytes) {
074: try {
075: ByteArrayInputStream bai = new ByteArrayInputStream(
076: clazzBytes);
077: TCObjectInputStream tci = new TCObjectInputStream(bai);
078:
079: String classIdentifier = tci.readString();
080: String genClassName = tci.readString();
081:
082: loader.defineClassFromBytes(genClassName, classId,
083: clazzBytes, clazzBytes.length - bai.available(),
084: bai.available());
085: addKnownClasses(classIdentifier, genClassName, classId);
086: } catch (Exception ex) {
087: throw new TCRuntimeException(ex);
088: }
089: }
090:
091: private void writeToDB(ClassSpec cs, byte[] data) {
092: try {
093: String classIdentifier = cs.getClassIdentifier();
094: String genClassName = cs.getGeneratedClassName();
095:
096: ByteArrayOutputStream bao = new ByteArrayOutputStream(
097: data.length + 1024);
098: TCObjectOutputStream tco = new TCObjectOutputStream(bao);
099: tco.writeString(classIdentifier);
100: tco.writeString(genClassName);
101: tco.write(data);
102: tco.flush();
103:
104: persistor.storeClass(cs.getClassID(), bao.toByteArray());
105: } catch (Exception ex) {
106: throw new TCRuntimeException(ex);
107: }
108: }
109:
110: public PhysicalManagedObjectState create(long strIdx,
111: ObjectID parentID, String className, String loaderDesc,
112: DNACursor cursor) {
113: ClassSpec cs = new ClassSpec(className, loaderDesc, strIdx);
114: String classIdentifier = cs.getClassIdentifier();
115: String generatedClassName = (String) knownClasses
116: .get(classIdentifier);
117: if (generatedClassName == null) {
118: Object lock = cs.getLock();
119: synchronized (lock) {
120: // Check again ! Double check locking is OK here as loader.load() is synchronized internally anyway
121: generatedClassName = (String) knownClasses
122: .get(classIdentifier);
123: if (generatedClassName == null) {
124: PhysicalManagedObjectState po = createNewClassAndInitializeObject(
125: parentID, cs, cursor);
126: return po;
127: }
128: }
129: }
130: return createNewObject(generatedClassName, parentID);
131: }
132:
133: public PhysicalManagedObjectState create(ObjectID parentID,
134: int classId) throws ClassNotFoundException {
135: Integer cid = new Integer(classId);
136: String className = (String) knownClasses.get(cid);
137: if (className == null) {
138: throw new ClassNotFoundException("Unknown Class Id :"
139: + classId + " Details : parent = " + parentID);
140: }
141: return createNewObject(className, parentID);
142: }
143:
144: public PhysicalManagedObjectState recreate(long classID,
145: ObjectID pid, String className, String loaderDesc,
146: DNACursor cursor, PhysicalManagedObjectState oldState) {
147: ClassSpec cs = new ClassSpec(className, loaderDesc, classID);
148: String classIdentifier = cs.getClassIdentifier();
149: String generatedClassName = (String) knownClasses
150: .get(classIdentifier);
151: Assert.assertNotNull(generatedClassName);
152: Object lock = cs.getLock();
153: synchronized (lock) {
154: PhysicalManagedObjectState newState = oldState;
155: if (!oldState.getClass().getName().equals(
156: generatedClassName)) {
157: // There is already a new version generated for this class, first try using that.
158: newState = createNewObject(generatedClassName, pid);
159: initNewStateFromOld(newState, oldState);
160: }
161: List deltaFields = findDeltaFields(newState, cursor);
162: if (deltaFields.isEmpty()) {
163: // This class is sufficient
164: return newState;
165: } else {
166: // This newly generated class subclasses the newState
167: cs.setSuperClassName(newState.getClass().getName());
168: PhysicalManagedObjectState latestState = createNewClassAndInitializeObject(
169: pid, cs, deltaFields);
170: return initNewStateFromOld(latestState, oldState);
171: }
172: }
173: }
174:
175: private PhysicalManagedObjectState initNewStateFromOld(
176: PhysicalManagedObjectState newState,
177: PhysicalManagedObjectState oldState) {
178: if (newState == oldState) {
179: return newState;
180: }
181: Map fields2Vals = oldState.addValues(new HashMap());
182: for (Iterator i = fields2Vals.entrySet().iterator(); i
183: .hasNext();) {
184: Map.Entry e = (Entry) i.next();
185: newState.set((String) e.getKey(), e.getValue());
186: }
187: return newState;
188: }
189:
190: private List findDeltaFields(PhysicalManagedObjectState state,
191: DNACursor cursor) {
192: try {
193: List deltaFields = new ArrayList();
194: Map fields2Values = new HashMap();
195: state.addValues(fields2Values);
196: cursor.reset();
197: while (cursor.next()) {
198: PhysicalAction action = cursor.getPhysicalAction();
199: if (!fields2Values.containsKey(action.getFieldName())) {
200: deltaFields.add(createFieldType(action, deltaFields
201: .size()));
202: }
203: }
204: return deltaFields;
205: } catch (Exception ex) {
206: throw new TCRuntimeException(ex);
207: } finally {
208: cursor.reset();
209: }
210: }
211:
212: /**
213: * This method creates an instance of already loaded class. The fields are not initialized.
214: */
215: private PhysicalManagedObjectState createNewObject(
216: String stateClassName, ObjectID parentID) {
217: try {
218: Class c = loader.loadClass(stateClassName);
219: Constructor constructor = c
220: .getConstructor(CONSTRUCTOR_PARAMS_CLASS);
221: PhysicalManagedObjectState po = (PhysicalManagedObjectState) constructor
222: .newInstance(CONSTRUCTOR_PARAMS);
223: initializeManagedObjectState(po, parentID);
224: return po;
225: } catch (Exception e) {
226: throw new TCRuntimeException(e);
227: }
228: }
229:
230: /**
231: * The object returned by this method has the parent Id set
232: */
233: private PhysicalManagedObjectState createNewClassAndInitializeObject(
234: ObjectID parentID, ClassSpec cs, DNACursor cursor) {
235: try {
236: List fields = new ArrayList(cursor.getActionCount());
237: cursor.reset();
238: while (cursor.next()) {
239: PhysicalAction action = cursor.getPhysicalAction();
240: fields.add(createFieldType(action, fields.size()));
241: }
242: return createNewClassAndInitializeObject(parentID, cs,
243: fields);
244: } catch (Exception ex) {
245: throw new TCRuntimeException(ex);
246: } finally {
247: cursor.reset();
248: }
249: }
250:
251: private PhysicalManagedObjectState createNewClassAndInitializeObject(
252: ObjectID parentID, ClassSpec cs, List fields) {
253: try {
254: int clazzId = getNextSequenceID();
255: cs.setGeneratedClassID(clazzId);
256: byte data[] = loader.createClassBytes(cs, parentID, fields);
257:
258: String generatedClassName = cs.getGeneratedClassName();
259: Class c = loader.defineClassFromBytes(generatedClassName,
260: clazzId, data, 0, data.length);
261: addKnownClasses(cs);
262: writeToDB(cs, data);
263: Constructor constructor = c
264: .getConstructor(CONSTRUCTOR_PARAMS_CLASS);
265: PhysicalManagedObjectState mo = (PhysicalManagedObjectState) constructor
266: .newInstance(CONSTRUCTOR_PARAMS);
267: initializeManagedObjectState(mo, parentID);
268: return mo;
269: } catch (Exception e) {
270: throw new TCRuntimeException(e);
271: }
272: }
273:
274: private void initializeManagedObjectState(
275: PhysicalManagedObjectState po, ObjectID parentID) {
276: po.setParentID(parentID);
277: }
278:
279: private synchronized int getNextSequenceID() {
280: return ++sequenceId;
281: }
282:
283: private FieldType createFieldType(PhysicalAction action, int id) {
284: String fieldName = action.getFieldName();
285: Object value = action.getObject();
286: return FieldType.create(fieldName, value, action.isReference(),
287: id);
288: }
289:
290: }
|