KickJava   Java API By Example, From Geeks To Geeks.

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


1 package org.apache.ojb.broker.core.proxy;
2
3 /* Copyright 2002-2005 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.PersistenceBrokerException;
26 import org.apache.ojb.broker.PersistenceBrokerFactory;
27 import org.apache.ojb.broker.PersistenceBrokerInternal;
28 import org.apache.ojb.broker.core.PersistenceBrokerThreadMapping;
29 import org.apache.ojb.broker.util.logging.LoggerFactory;
30
31 /**
32  * Abstract implementation for the indirection handler used by ojb's proxies.
33  *
34  * @version $Id$
35  */

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

46     private Identity _id = null;
47     /** The materialization listeners */
48     private transient ArrayList JavaDoc _listeners;
49
50     /**
51      * Creates a new indirection handler for the indicated object.
52      *
53      * @param brokerKey
54      * The key of the persistence broker
55      * @param id
56      * The identity of the subject
57      */

58     public AbstractIndirectionHandler(PBKey brokerKey, Identity id)
59     {
60         setBrokerKey(brokerKey);
61         setIdentity(id);
62     }
63
64     /**
65      * Returns the identity of the subject.
66      *
67      * @return The identity
68      */

69     public Identity getIdentity()
70     {
71         return _id;
72     }
73
74     /**
75      * Sets the identity of the subject of this indirection handler.
76      *
77      * @param identity
78      */

79     protected void setIdentity(Identity identity)
80     {
81         _id = identity;
82     }
83
84     /**
85      * Returns the key of the persistence broker used by this indirection
86      * handler.
87      *
88      * @return The broker key
89      */

90     public PBKey getBrokerKey()
91     {
92         return _brokerKey;
93     }
94
95     /**
96      * Sets the key of the persistence broker used by this indirection handler.
97      *
98      * @param brokerKey
99      * The broker key
100      */

101     protected void setBrokerKey(PBKey brokerKey)
102     {
103         _brokerKey = brokerKey;
104     }
105
106     /**
107      * Adds a materialization listener.
108      *
109      * @param listener
110      * The listener to add
111      */

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

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

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

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

186     protected TemporaryBrokerWrapper getBroker() throws PBFactoryException
187     {
188         PersistenceBrokerInternal broker;
189         boolean needsClose = false;
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) || broker.isClosed())
205         {
206             broker = (PersistenceBrokerInternal) PersistenceBrokerFactory.createPersistenceBroker(getBrokerKey());
207             /** Specifies whether we obtained a fresh broker which we have to close after we used it */
208             needsClose = true;
209         }
210         return new TemporaryBrokerWrapper(broker, needsClose);
211     }
212
213     /**
214      * [Copied from {@link java.lang.reflect.InvocationHandler}]:<br/>
215      * Processes a method invocation on a proxy instance and returns the result.
216      * This method will be invoked on an invocation handler when a method is
217      * invoked on a proxy instance that it is associated with.
218      *
219      * @param proxy
220      * The proxy instance that the method was invoked on
221      *
222      * @param method
223      * The <code>Method</code> instance corresponding to the
224      * interface method invoked on the proxy instance. The declaring
225      * class of the <code>Method</code> object will be the
226      * interface that the method was declared in, which may be a
227      * superinterface of the proxy interface that the proxy class
228      * inherits the method through.
229      *
230      * @param args
231      * An array of objects containing the values of the arguments
232      * passed in the method invocation on the proxy instance, or
233      * <code>null</code> if interface method takes no arguments.
234      * Arguments of primitive types are wrapped in instances of the
235      * appropriate primitive wrapper class, such as
236      * <code>java.lang.Integer</code> or
237      * <code>java.lang.Boolean</code>.
238      *
239      * @return The value to return from the method invocation on the proxy
240      * instance. If the declared return type of the interface method is
241      * a primitive type, then the value returned by this method must be
242      * an instance of the corresponding primitive wrapper class;
243      * otherwise, it must be a type assignable to the declared return
244      * type. If the value returned by this method is <code>null</code>
245      * and the interface method's return type is primitive, then a
246      * <code>NullPointerException</code> will be thrown by the method
247      * invocation on the proxy instance. If the value returned by this
248      * method is otherwise not compatible with the interface method's
249      * declared return type as described above, a
250      * <code>ClassCastException</code> will be thrown by the method
251      * invocation on the proxy instance.
252      *
253      * @throws PersistenceBrokerException
254      * The exception to throw from the method invocation on the
255      * proxy instance. The exception's type must be assignable
256      * either to any of the exception types declared in the
257      * <code>throws</code> clause of the interface method or to
258      * the unchecked exception types
259      * <code>java.lang.RuntimeException</code> or
260      * <code>java.lang.Error</code>. If a checked exception is
261      * thrown by this method that is not assignable to any of the
262      * exception types declared in the <code>throws</code> clause
263      * of the interface method, then an
264      * {@link java.lang.reflect.UndeclaredThrowableException}
265      * containing the exception that was thrown by this method will
266      * be thrown by the method invocation on the proxy instance.
267      *
268      * @see java.lang.reflect.UndeclaredThrowableException
269      */

270     public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args)
271     {
272         Object JavaDoc subject;
273         String JavaDoc methodName = method.getName();
274
275         try
276         {
277             // [andrew clute]
278
// short-circuit any calls to a finalize methjod if the subject
279
// has not been retrieved yet
280
if ("finalize".equals(methodName) && _realSubject == null)
281             {
282                 return null;
283             }
284
285             // [andrew clute]
286
// When trying to serialize a proxy, we need to determine how to
287
// handle it
288
if ("writeReplace".equals(methodName))
289             {
290                 if (_realSubject == null)
291                 {
292                     // Unmaterialized proxies are replaced by simple
293
// serializable
294
// objects that can be unserialized without classloader
295
// issues
296
return generateSerializableProxy();
297                 } else
298                 {
299                     // Materiliazed objects should be passed back as they might
300
// have
301
// been mutated
302
return getRealSubject();
303                 }
304             }
305
306             // [tomdz]
307
// Previously the hashcode of the identity would have been used
308
// but this requires a compatible hashCode implementation in the
309
// proxied object (which is somewhat unlikely, even the default
310
// hashCode implementation does not fulfill this requirement)
311
// for those that require this behavior, a custom indirection
312
// handler can be used, or the hashCode of the identity
313
/*
314              * if ("hashCode".equals(methodName)) { return new
315              * Integer(_id.hashCode()); }
316              */

317
318             // [tomdz]
319
// this would handle toString differently for non-materialized
320
// proxies
321
// (to avoid materialization due to logging)
322
// however toString should be a normal business method which
323
// materializes the proxy
324
// if this is not desired, then the ProxyHandler.toString(Object)
325
// method
326
// should be used instead (e.g. for logging within OJB)
327
/*
328              * if ((realSubject == null) && "toString".equals(methodName)) {
329              * return "unmaterialized proxy for " + id; }
330              */

331
332             // BRJ: make sure that the object to be compared is a real object
333
// otherwise equals may return false.
334
if ("equals".equals(methodName) && args[0] != null)
335             {
336                 TemporaryBrokerWrapper tmp = getBroker();
337                 try
338                 {
339                     args[0] = tmp.broker.getProxyFactory().getRealObject(args[0]);
340                 }
341                 finally
342                 {
343                     tmp.close();
344                 }
345             }
346
347             if ("getIndirectionHandler".equals(methodName) && args[0] != null)
348             {
349                 return this;
350             }
351
352             subject = getRealSubject();
353             return method.invoke(subject, args);
354             // [olegnitz] I've changed the following strange lines
355
// to the above one. Why was this done in such complicated way?
356
// Is it possible that subject doesn't implement the method's
357
// interface?
358
// Method m = subject.getClass().getMethod(method.getName(),
359
// method.getParameterTypes());
360
// return m.invoke(subject, args);
361
} catch (Exception JavaDoc ex)
362         {
363             throw new PersistenceBrokerException("Error invoking method " + method.getName(), ex);
364         }
365     }
366
367     /**
368      * Returns the proxies real subject. The subject will be materialized if
369      * necessary.
370      *
371      * @return The subject
372      */

373     public Object JavaDoc getRealSubject() throws PersistenceBrokerException
374     {
375         if (_realSubject == null)
376         {
377             beforeMaterialization();
378             _realSubject = materializeSubject();
379             afterMaterialization();
380         }
381         return _realSubject;
382     }
383
384     /**
385      * [olegnitz] This looks stupid, but is really necessary for OTM: the
386      * materialization listener replaces the real subject by its clone to ensure
387      * transaction isolation. Is there a better way to do this?
388      */

389     public void setRealSubject(Object JavaDoc object)
390     {
391         _realSubject = object;
392     }
393
394     /**
395      * Retrieves the real subject from the underlying RDBMS. Override this
396      * method if the object is to be materialized in a specific way.
397      *
398      * @return The real subject of the proxy
399      */

400     protected synchronized Object JavaDoc materializeSubject() throws PersistenceBrokerException
401     {
402         TemporaryBrokerWrapper tmp = getBroker();
403         try
404         {
405             Object JavaDoc realSubject = tmp.broker.getObjectByIdentity(_id);
406             if (realSubject == null)
407             {
408                 LoggerFactory.getLogger(IndirectionHandler.class).warn(
409                         "Can not materialize object for Identity " + _id + " - using PBKey " + getBrokerKey());
410             }
411             return realSubject;
412         } catch (Exception JavaDoc ex)
413         {
414             throw new PersistenceBrokerException(ex);
415         } finally
416         {
417             tmp.close();
418         }
419     }
420
421     /**
422      * Determines whether the real subject already has been materialized.
423      *
424      * @return <code>true</code> if the real subject has already been loaded
425      */

426     public boolean alreadyMaterialized()
427     {
428         return _realSubject != null;
429     }
430
431     /**
432      * Generate a simple object that is serializable and placeholder for
433      * proxies.
434      *
435      */

436     private Object JavaDoc generateSerializableProxy()
437     {
438         return new OJBSerializableProxy(getIdentity().getObjectsRealClass(), this);
439     }
440
441     //===================================================================
442
// inner class
443
//===================================================================
444
/**
445      * wrapper class for temporary used broker instances.
446      */

447     static final class TemporaryBrokerWrapper
448     {
449         /** Specifies whether we obtained a fresh broker which we have to close after we used it */
450         boolean needsClose;
451         PersistenceBrokerInternal broker;
452
453         public TemporaryBrokerWrapper(PersistenceBrokerInternal broker, boolean needsClose)
454         {
455             this.broker = broker;
456             this.needsClose = needsClose;
457         }
458
459         /**
460          * Cleanup the used broker instance, it's mandatory to call
461          * this method after use.
462          */

463         public void close()
464         {
465             if(needsClose)
466             {
467                 broker.close();
468             }
469         }
470     }
471 }
472
Popular Tags