View Javadoc

1   package org.sourceforge.jemm.client;
2   
3   import org.sourceforge.jemm.JEMMInternalException;
4   import org.sourceforge.jemm.client.events.LockTracer;
5   import org.sourceforge.jemm.client.events.StackTracer;
6   import org.sourceforge.jemm.lifecycle.ConstructorListener;
7   import org.sourceforge.jemm.lifecycle.LifecycleEvent;
8   import org.sourceforge.jemm.lifecycle.LockEvent;
9   import org.sourceforge.jemm.lifecycle.MethodEvent;
10  import org.sourceforge.jemm.lifecycle.MethodListener;
11  import org.sourceforge.jemm.lifecycle.TypeConstructorListener;
12  import org.sourceforge.jemm.util.ConstructorContext;
13  import org.sourceforge.jemm.util.ContextStack;
14  import org.sourceforge.jemm.util.JEMMObject;
15  import org.sourceforge.jemm.util.JEMMType;
16  
17  /**
18   * A lifecycle listener which manages the state of the JEMMObject, such as the
19   * object values, the JEMM ID and the ShadowObject.
20   * 
21   * <pre> 
22  Hierarchy of class constructors
23  -------------------------------
24  
25  pre - stack the class
26  super -> 
27  	pre - stack the class
28  	super
29  	begin - associated the the object with stacked class
30  
31  	end/fail
32  begin
33  end/fail
34  ---
35  END/FAIL ALGORITHM
36  pop
37  if(peek is a class in the hierarchy of current OBJ AND has no object)
38  	do nothing
39  else //must be a different object on the stack
40  	synchronize
41  ---
42  
43  
44  Nested Construction of objects
45  ------------------------------
46  
47  pre
48  super
49  begin
50  	pre
51  	begin
52  	end/fail
53  end/fail
54  </pre>
55   * 
56   * @author Paul Keeble
57   * 
58   */
59  public class LifecycleListenerImpl implements ConstructorListener,
60  		MethodListener,TypeConstructorListener {
61  
62  	ObjectDatabase db;
63  	ObjectDatabase whenLockedDB;
64  
65  	private JEMMObjectCreator objectCreator;
66  	private StackTracer stackTracer;
67  	private LockTracer lockTracer;
68  	
69  	public LifecycleListenerImpl(ObjectDatabase db,StackTracer st, LockTracer lt) {
70  		this.db = db;
71  		this.whenLockedDB = new AlwaysRefreshObjectDatabase(db);
72  		
73  		this.stackTracer = st;
74  		this.lockTracer = lt;
75  	}
76  
77  	/**
78  	 * 
79  	 */
80  	@Override
81  	public void preConstructor(String className) {
82  		ContextStack.pushReference(new ConstructorContext(className));
83  	}
84  	
85  	/**
86  	 * Register the class of the object.
87  	 * 
88  	 * Gets the ID for a new object and sets it back to the original object
89  	 * along with the ShadowObject.
90  	 * 
91  	 * If the constructor for the same object but a super class is called this should
92  	 * be ignored and no further registrations or IDs retrieved.
93  	 */
94  	@Override
95  	public void beginConstructor(LifecycleEvent e) {
96  		JEMMObject origin = e.getOrigin();
97  		
98  		ConstructorContext cc = (ConstructorContext)ContextStack.peekReference();
99  		cc.associateObject(origin);
100 		
101 		objectCreator.initialise(origin);
102 	}
103 
104 	public void setObjectCreator(JEMMObjectCreator creator) {
105 		this.objectCreator = creator;
106 	}
107 	
108 	@Override public void typeConstructed(JEMMType typeInstance) {
109 
110 		objectCreator.initialise(typeInstance);
111     }
112 	
113 	/**
114 	 * Removes the context for the constructor from the stack.
115 	 */
116 	@Override
117 	public void failedConstruction(LifecycleEvent e) {
118 		ContextStack.popReference();
119 	}
120 
121 	/**
122 	 * Synchronise the state of the Object with the database and clear any
123 	 * object fields of their values into the ShadowData. The fields should only
124 	 * be saved/cleared if the Object is being exited and hence this is the last
125 	 * endConstructor being called for this Objects creation.
126 	 */
127 	@Override
128 	public void endConstructor(LifecycleEvent e) {
129 		ConstructorContext context = (ConstructorContext)ContextStack.popReference();
130 		ConstructorContext peekedContext = (ConstructorContext)ContextStack.peekReference();
131 
132 		JEMMObject origin = e.getOrigin();
133 		if(context==null || context.getObject()!=origin)
134 			throw new JEMMInternalException("ContextStack corrupted");
135 		
136 		if(peekedContext!=null && !peekedContext.hasObject() && isParent(context.getClazz(),peekedContext.getClazz())) {
137 			//this is a parent call super class constructor call, we don't need to synchronize
138 			return;
139 		}
140 		
141 		ObjectAccessor oa = new ObjectAccessor(origin,db);
142 		oa.finaliseAllFields();
143 		db.synchroniseObject(origin);
144 	}
145 	
146 	private boolean isParent(String parent,String child) {
147 		try {
148 			Class<?> parentClass = Class.forName(parent);
149 			Class<?> childClass = Class.forName(child);
150 			return childClass.getSuperclass().equals(parentClass);
151 		} catch (ClassNotFoundException e) {
152 			throw new JEMMInternalException("Unable to convert to class " + e.getMessage());
153 		}
154 	}
155 
156 	/**
157 	 * Acquire a lock in the database
158 	 */
159 	@Override
160 	public void beginLock(LockEvent e) {
161 		lockTracer.beginLock(e.getOrigin(), e.getLock());
162 	}
163 
164 	/**
165 	 * Release a lock in the database
166 	 */
167 	@Override
168 	public void endLock(LockEvent e) {
169 		lockTracer.endLock(e.getOrigin(), e.getLock());
170 	}
171 
172 	/**
173 	 * Register the class
174 	 * 
175 	 * Setup the required object fields that will be used by the method if it is
176 	 * the first time a method is entered. If an attribute has already been
177 	 * setup then the value (even if it has changed) is left in place.
178 	 * 
179 	 * Track the call stack to allow the exit method to determine if it can save
180 	 * the values.
181 	 */
182 	@Override
183 	public void entityEntered(MethodEvent e) {
184 		stackTracer.enterMethod(e.getOrigin(), new Descriptor(e.getDescriptor()));
185 	}
186 
187 	/**
188 	 * Captures the object state and saves it.
189 	 * 
190 	 * This is only done if the Object is actually being exited by this call, if
191 	 * it is not then the fields should be left in tact and no storage of
192 	 * attributes done until this is called the last time for a method entry.
193 	 * 
194 	 * Release all the JEMMObject fields that were initialised by entityEntered
195 	 */
196 	@Override
197 	public void entityExited(MethodEvent e) {
198 		stackTracer.exitMethod(e.getOrigin(), new Descriptor(e.getDescriptor()));
199 	}
200 
201 }