KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > core > proxy > IndirectionHandlerDefaultImpl


1 package org.apache.ojb.broker.core.proxy;
2
3 /* Copyright 2002-2004 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import java.lang.reflect.Method JavaDoc;
19 import java.util.ArrayList JavaDoc;
20
21 import org.apache.ojb.broker.Identity;
22 import org.apache.ojb.broker.OJBRuntimeException;
23 import org.apache.ojb.broker.PBFactoryException;
24 import org.apache.ojb.broker.PBKey;
25 import org.apache.ojb.broker.PersistenceBroker;
26 import org.apache.ojb.broker.PersistenceBrokerException;
27 import org.apache.ojb.broker.PersistenceBrokerFactory;
28 import org.apache.ojb.broker.core.PersistenceBrokerThreadMapping;
29 import org.apache.ojb.broker.util.logging.LoggerFactory;
30
31 /**
32  * Default implementation for the indirection handler used by ojb's proxies.
33  *
34  * @version $Id: IndirectionHandlerDefaultImpl.java,v 1.6.2.2 2005/03/22 15:43:30 arminw Exp $
35  */

36 public class IndirectionHandlerDefaultImpl implements IndirectionHandler
37 {
38     static final long serialVersionUID = -1993879565033755826L;
39     
40     /** Reference to the used PersistenceBroker */
41     private transient PersistenceBroker _broker = null;
42     /** The key for acquiring the above broker */
43     private PBKey _brokerKey;
44     /** The real subject which this is hidden by the proxy */
45     private Object JavaDoc _realSubject = null;
46     /** Represents the identity of the real subject. When the real subject is not
47      * yet materialized, it can be loaded from the underlying db by this identity object */

48     private Identity _id = null;
49     /** The materialization listeners */
50     private transient ArrayList JavaDoc _listeners;
51     /*
52     arminw:
53     only close broker instance if we get it from PBF, do
54     not close if we obtain from PBThreadMapping
55     TODO: Find a better solution
56     */

57     private boolean _needsClose;
58
59     /**
60      * Creates a new indirection handler for the indicated object.
61      *
62      * @param brokerKey The key of the persistence broker
63      * @param id The identity of the subject
64      */

65     public IndirectionHandlerDefaultImpl(PBKey brokerKey, Identity id)
66     {
67         setBrokerKey(brokerKey);
68         setIdentity(id);
69     }
70
71     /**
72      * Returns the identity of the subject.
73      *
74      * @return The identity
75      */

76     public Identity getIdentity()
77     {
78         return _id;
79     }
80
81     /**
82      * Sets the identity of the subject of this indirection handler.
83      *
84      * @param identity
85      */

86     protected void setIdentity(Identity identity)
87     {
88         _id = identity;
89     }
90
91     /**
92      * Returns the key of the persistence broker used by this indirection handler.
93      *
94      * @return The broker key
95      */

96     public PBKey getBrokerKey()
97     {
98         return _brokerKey;
99     }
100
101     /**
102      * Sets the key of the persistence broker used by this indirection handler.
103      *
104      * @param brokerKey The broker key
105      */

106     protected void setBrokerKey(PBKey brokerKey)
107     {
108         _brokerKey = brokerKey;
109     }
110
111     /**
112      * Adds a materialization listener.
113      *
114      * @param listener The listener to add
115      */

116     public synchronized void addListener(MaterializationListener listener)
117     {
118         if (_listeners == null)
119         {
120             _listeners = new ArrayList JavaDoc();
121         }
122         // add listener only once
123
if(!_listeners.contains(listener))
124         {
125             _listeners.add(listener);
126         }
127     }
128
129     /**
130      * Removes a materialization listener.
131      *
132      * @param listener The listener to remove
133      */

134     public synchronized void removeListener(MaterializationListener listener)
135     {
136         if (_listeners != null)
137         {
138             _listeners.remove(listener);
139         }
140     }
141
142     /**
143      * Calls beforeMaterialization on all registered listeners in the reverse order
144      * of registration.
145      */

146     protected void beforeMaterialization()
147     {
148         if (_listeners != null)
149         {
150             MaterializationListener listener;
151
152             for (int idx = _listeners.size() - 1; idx >= 0; idx--)
153             {
154                 listener = (MaterializationListener)_listeners.get(idx);
155                 listener.beforeMaterialization(this, _id);
156             }
157         }
158     }
159
160     /**
161      * Calls afterMaterialization on all registered listeners in the reverse order
162      * of registration.
163      */

164     protected void afterMaterialization()
165     {
166         if (_listeners != null)
167         {
168             MaterializationListener listener;
169
170             // listeners may remove themselves during the afterMaterialization callback.
171
// thus we must iterate through the listeners vector from back to front
172
// to avoid index problems.
173
for (int idx = _listeners.size() - 1; idx >= 0; idx--)
174             {
175                 listener = (MaterializationListener)_listeners.get(idx);
176                 listener.afterMaterialization(this, _realSubject);
177             }
178         }
179     }
180
181     /**
182      * Gets the persistence broker used by this indirection handler.
183      * If no PBKey is available a runtime exception will be thrown.
184      *
185      * @return a PersistenceBroker
186      */

187     protected synchronized PersistenceBroker getBroker() throws PBFactoryException
188     {
189         PersistenceBroker broker;
190
191         if (getBrokerKey() == null)
192         {
193             /*
194             arminw:
195             if no PBKey is set we throw an exception, because we don't
196             know which PB (connection) should be used.
197             */

198             throw new OJBRuntimeException("Can't find associated PBKey. Need PBKey to obtain a valid" +
199                                           "PersistenceBroker instance from intern resources.");
200         }
201         // first try to use the current threaded broker to avoid blocking
202
broker = PersistenceBrokerThreadMapping.currentPersistenceBroker(getBrokerKey());
203         // current broker not found, create a intern new one
204
if (broker == null)
205         {
206             if (_broker == null)
207             {
208                 _broker = PersistenceBrokerFactory.createPersistenceBroker(getBrokerKey());
209                 // TODO: Better way?
210
_needsClose = true;
211                 broker = _broker;
212             }
213         }
214         return broker;
215     }
216
217     /**
218      * Release the PersistenceBroker instance currently used.
219      */

220     protected void releaseBroker()
221     {
222         if (_broker != null && _needsClose)
223         {
224             _needsClose = false;
225             _broker.close();
226             _broker = null;
227         }
228     }
229
230     /**
231      * [Copied from {@link java.lang.reflect.InvocationHandler}]:<br/>
232      * Processes a method invocation on a proxy instance and returns
233      * the result. This method will be invoked on an invocation handler
234      * when a method is invoked on a proxy instance that it is
235      * associated with.
236      *
237      * @param proxy The proxy instance that the method was invoked on
238      *
239      * @param method The <code>Method</code> instance corresponding to
240      * the interface method invoked on the proxy instance. The declaring
241      * class of the <code>Method</code> object will be the interface that
242      * the method was declared in, which may be a superinterface of the
243      * proxy interface that the proxy class inherits the method through.
244      *
245      * @param args An array of objects containing the values of the
246      * arguments passed in the method invocation on the proxy instance,
247      * or <code>null</code> if interface method takes no arguments.
248      * Arguments of primitive types are wrapped in instances of the
249      * appropriate primitive wrapper class, such as
250      * <code>java.lang.Integer</code> or <code>java.lang.Boolean</code>.
251      *
252      * @return The value to return from the method invocation on the
253      * proxy instance. If the declared return type of the interface
254      * method is a primitive type, then the value returned by
255      * this method must be an instance of the corresponding primitive
256      * wrapper class; otherwise, it must be a type assignable to the
257      * declared return type. If the value returned by this method is
258      * <code>null</code> and the interface method's return type is
259      * primitive, then a <code>NullPointerException</code> will be
260      * thrown by the method invocation on the proxy instance. If the
261      * value returned by this method is otherwise not compatible with
262      * the interface method's declared return type as described above,
263      * a <code>ClassCastException</code> will be thrown by the method
264      * invocation on the proxy instance.
265      *
266      * @throws PersistenceBrokerException The exception to throw from the method
267      * invocation on the proxy instance. The exception's type must be
268      * assignable either to any of the exception types declared in the
269      * <code>throws</code> clause of the interface method or to the
270      * unchecked exception types <code>java.lang.RuntimeException</code>
271      * or <code>java.lang.Error</code>. If a checked exception is
272      * thrown by this method that is not assignable to any of the
273      * exception types declared in the <code>throws</code> clause of
274      * the interface method, then an
275      * {@link java.lang.reflect.UndeclaredThrowableException} containing the
276      * exception that was thrown by this method will be thrown by the
277      * method invocation on the proxy instance.
278      *
279      * @see java.lang.reflect.UndeclaredThrowableException
280      */

281     public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args)
282     {
283         Object JavaDoc subject = null;
284         String JavaDoc methodName = method.getName();
285
286         try
287         {
288             // [tomdz]
289
// Previously the hashcode of the identity would have been used
290
// but this requires a compatible hashCode implementation in the
291
// proxied object (which is somewhat unlikely, even the default
292
// hashCode implementation does not fulfill this requirement)
293
// for those that require this behavior, a custom indirection
294
// handler can be used, or the hashCode of the identity
295
/*
296             if ("hashCode".equals(methodName))
297             {
298                 return new Integer(_id.hashCode());
299             }
300             */

301
302             // [tomdz]
303
// this would handle toString differently for non-materialized proxies
304
// (to avoid materialization due to logging)
305
// however toString should be a normal business method which
306
// materializes the proxy
307
// if this is not desired, then the ProxyHandler.toString(Object) method
308
// should be used instead (e.g. for logging within OJB)
309
/*
310             if ((realSubject == null) && "toString".equals(methodName))
311             {
312                 return "unmaterialized proxy for " + id;
313             }
314             */

315             
316             // BRJ: make sure that the object to be compared is a real object
317
// otherwise equals may return false.
318
if ("equals".equals(methodName) && args[0] != null)
319             {
320                 Object JavaDoc otherObj = ProxyHelper.getRealObject(args[0]);
321                 args[0] = otherObj;
322             }
323             
324             subject = getRealSubject();
325             return method.invoke(subject, args);
326             // [olegnitz] I've changed the following strange lines
327
// to the above one. Why was this done in such complicated way?
328
// Is it possible that subject doesn't implement the method's interface?
329
// Method m = subject.getClass().getMethod(method.getName(), method.getParameterTypes());
330
// return m.invoke(subject, args);
331
}
332         catch (Exception JavaDoc ex)
333         {
334             throw new PersistenceBrokerException("Error invoking method "+method.getName(), ex);
335         }
336     }
337
338     /**
339      * Returns the proxies real subject. The subject will be materialized if necessary.
340      *
341      * @return The subject
342      */

343     public Object JavaDoc getRealSubject() throws PersistenceBrokerException
344     {
345         if (_realSubject == null)
346         {
347             beforeMaterialization();
348             _realSubject = materializeSubject();
349             afterMaterialization();
350         }
351         return _realSubject;
352     }
353
354     /**
355      * [olegnitz] This looks stupid, but is really necessary for OTM:
356      * the materialization listener replaces the real subject
357      * by its clone to ensure transaction isolation.
358      * Is there a better way to do this?
359      */

360     public void setRealSubject(Object JavaDoc object)
361     {
362         _realSubject = object;
363     }
364
365     /**
366      * Retrieves the real subject from the underlying RDBMS. Override this method if
367      * the object is to be materialized in a specific way.
368      *
369      * @return The real subject of the proxy
370      */

371     protected synchronized Object JavaDoc materializeSubject() throws PersistenceBrokerException
372     {
373         try
374         {
375             Object JavaDoc realSubject = getBroker().getObjectByIdentity(_id);
376             if (realSubject == null)
377             {
378                 LoggerFactory.getLogger(IndirectionHandler.class).warn("Can not materialize object for Identity " + _id + " - using PBKey " + _brokerKey);
379             }
380             return realSubject;
381         }
382         catch (Exception JavaDoc ex)
383         {
384             throw new PersistenceBrokerException(ex);
385         }
386         finally
387         {
388             releaseBroker();
389         }
390     }
391
392     /**
393      * Determines whether the real subject already has been materialized.
394      *
395      * @return <code>true</code> if the real subject has already been loaded
396      */

397     public boolean alreadyMaterialized()
398     {
399         return _realSubject != null;
400     }
401 }
402
Popular Tags