KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openejb > core > ivm > BaseEjbProxyHandler


1 /**
2  * Redistribution and use of this software and associated documentation
3  * ("Software"), with or without modification, are permitted provided
4  * that the following conditions are met:
5  *
6  * 1. Redistributions of source code must retain copyright
7  * statements and notices. Redistributions must also contain a
8  * copy of this document.
9  *
10  * 2. Redistributions in binary form must reproduce the
11  * above copyright notice, this list of conditions and the
12  * following disclaimer in the documentation and/or other
13  * materials provided with the distribution.
14  *
15  * 3. The name "Exolab" must not be used to endorse or promote
16  * products derived from this Software without prior written
17  * permission of Exoffice Technologies. For written permission,
18  * please contact info@exolab.org.
19  *
20  * 4. Products derived from this Software may not be called "Exolab"
21  * nor may "Exolab" appear in their names without prior written
22  * permission of Exoffice Technologies. Exolab is a registered
23  * trademark of Exoffice Technologies.
24  *
25  * 5. Due credit should be given to the Exolab Project
26  * (http://www.exolab.org/).
27  *
28  * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32  * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39  * OF THE POSSIBILITY OF SUCH DAMAGE.
40  *
41  * Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved.
42  *
43  * $Id: BaseEjbProxyHandler.java 2517 2006-02-26 23:06:41Z dblevins $
44  */

45 package org.openejb.core.ivm;
46
47 import java.io.IOException JavaDoc;
48 import java.io.ObjectStreamException JavaDoc;
49 import java.io.Serializable JavaDoc;
50 import java.io.ByteArrayOutputStream JavaDoc;
51 import java.io.ObjectOutputStream JavaDoc;
52 import java.io.ByteArrayInputStream JavaDoc;
53 import java.io.ObjectInputStream JavaDoc;
54 import java.lang.reflect.Method JavaDoc;
55 import java.lang.reflect.UndeclaredThrowableException JavaDoc;
56 import java.rmi.MarshalledObject JavaDoc;
57 import java.rmi.NoSuchObjectException JavaDoc;
58 import java.rmi.RemoteException JavaDoc;
59 import java.util.HashSet JavaDoc;
60 import java.util.Hashtable JavaDoc;
61 import java.util.Iterator JavaDoc;
62
63 import javax.ejb.EJBException JavaDoc;
64
65 import org.openejb.OpenEJB;
66 import org.openejb.RpcContainer;
67 import org.openejb.core.DeploymentInfo;
68 import org.openejb.core.ThreadContext;
69 import org.openejb.util.proxy.InvocationHandler;
70 import org.openejb.util.proxy.ProxyManager;
71
72 /**
73  * This InvocationHandler and its proxy are serializable and can be used by
74  * HomeHandle, Handle, and MetaData to persist and revive handles. It maintains
75  * its original client identity which allows the container to be more discerning about
76  * allowing the revieed proxy to be used. See StatefulContaer manager for more details.
77  *
78  * @author <a HREF="mailto:david.blevins@visi.com">David Blevins</a>
79  * @author <a HREF="mailto:Richard@Monson-Haefel.com">Richard Monson-Haefel</a>
80  */

81 public abstract class BaseEjbProxyHandler implements InvocationHandler, Serializable JavaDoc {
82     /**
83     * keeps track of live BaseEjbProxyHanlders in this VM. So that related handlers can be removed
84     * if necessary.
85     *
86     * Currently this registry is only used to track live EjbObjectProxyHandlers. The EjbObjectProxyHandlers
87     * are tracked to allow remove() operations and invalidate exceptions to be propagated to the proper
88     * handler instances.
89     *
90     * There are several scenarios where this is useful:
91     * <ul>
92     * <li>
93     * If an EJBHome.remove( )method is invoked, the EjbHomeProxyHandler will use
94     * this registry to notify the EjbObjectProxyHandler associated with the removed identity
95     * that the EjbObjectProxyHandler should invalidate itself.
96     * <li>
97     * When two EjbObjectProxy handlers are both associated with the same bean identity and one is
98     * removed, the EjbObjectProxyHandler executing the remove will notify the other EjbObjectProxyHandler
99     * objects associated with the same identity that they are to be invalidated.
100     * <li>
101     * When an EjbObjectProxyHanlder performs an operation that results in a an InvalidateReferenceException
102     * The EjbObjectProxyHandler will use the registry to ensure that all EjbObjectProxyHandlers associated
103     * the identity are invalidated.
104     */

105     protected static final Hashtable JavaDoc liveHandleRegistry = new Hashtable JavaDoc();
106
107     /**
108      * The unique id of the bean deployment that this stub handler represents.
109      */

110     public final Object JavaDoc deploymentID;
111
112     /**
113      * The primary key of the bean deployment or null if the deployment is a bean type that doesn't require a primary key
114      */

115     public final Object JavaDoc primaryKey;
116
117     /**
118      */

119     public boolean inProxyMap = false;
120
121     /**
122      * The DeployemtnInfo object if the bean deployment that this stub handler represents.
123      */

124     public transient DeploymentInfo deploymentInfo;
125
126     /**
127      * The RpcContainer that the bean deployment this stub hanlder represents is deployed in.
128      */

129     public transient RpcContainer container;
130
131     protected boolean isInvalidReference = false;
132     
133     /*
134     * The EJB 1.1 specification requires that arguments and return values between beans adhere to the
135     * Java RMI copy semantics which requires that the all arguments be passed by value (copied) and
136     * never passed as references. However, it is possible for the system administrator to turn off the
137     * copy operation so that arguments and return values are passed by reference as performance optimization.
138     * Simply setting the org.openejb.core.EnvProps.INTRA_VM_COPY property to FALSE will cause this variable to
139     * set to false, and therefor bypass the copy operations in the invoke( ) method of this class; arguments
140     * and return values will be passed by reference not value.
141     *
142     * This property is, by default, always TRUE but it can be changed to FALSE by setting it as a System property
143     * or a property of the Property argument when invoking OpenEJB.init(props). This variable is set to that
144     * property in the static block for this class.
145     */

146     protected boolean doIntraVmCopy;
147     private boolean isLocal;
148
149     /**
150      * Constructs a BaseEjbProxyHandler representing the specifed bean deployment.
151      *
152      * @param container The Container that the bean deployment this stub hanlder represents is deployed in.
153      * @param pk The primary key of the bean deployment or null if the deployment is a bean type that doesn't require a primary key.
154      * @param depID The unique id of the bean deployment that this stub handler will represent.
155      */

156     public BaseEjbProxyHandler(RpcContainer container, Object JavaDoc pk, Object JavaDoc depID){
157        this.container = container;
158        this.primaryKey = pk;
159        this.deploymentID = depID;
160        this.deploymentInfo = (org.openejb.core.DeploymentInfo)container.getDeploymentInfo(depID);
161        
162        String JavaDoc value = org.openejb.OpenEJB.getInitProps().getProperty("openejb.localcopy");
163        if ( value == null ) {
164            value = org.openejb.OpenEJB.getInitProps().getProperty(org.openejb.core.EnvProps.INTRA_VM_COPY);
165        }
166        if(value == null){
167            value = System.getProperty("openejb.localcopy");
168        }
169        if(value == null){
170            value = System.getProperty(org.openejb.core.EnvProps.INTRA_VM_COPY);
171        }
172        doIntraVmCopy = value==null || !value.equalsIgnoreCase("FALSE");
173     }
174     /**
175      * Invoked by the ObjectInputStream during desrialization of this stub handler. In this method the stub handler resets the deploymentInfo and container member variables.
176      *
177      * @param in The ObjectInputStream that has called this callback method and is deserializing this stub handler.
178      * @exception java.io.IOException
179      * If there is a problem reading this object from the InputStream
180      * @exception ClassNotFoundException
181      * If the class definition of an object refernced by this stub handler cannot be found.
182      * @exception NoSuchMethodException
183      */

184     private void readObject(java.io.ObjectInputStream JavaDoc in)
185     throws java.io.IOException JavaDoc,ClassNotFoundException JavaDoc, NoSuchMethodException JavaDoc{
186
187         in.defaultReadObject();
188
189         deploymentInfo = (org.openejb.core.DeploymentInfo)OpenEJB.getDeploymentInfo(deploymentID);
190         container = (RpcContainer)deploymentInfo.getContainer();
191     }
192     /**
193      * Checks if the caller of the specified method is authorized to access and execute it.
194      *
195      * Relies on the SecurityService assigned to the Container that this bean deployment is in to determine if the caller is authorized.
196      *
197      * @param method The method the caller is attempting to execute.
198      * @exception org.openejb.OpenEJBException
199      * If the caller does bot have adequate authorization to execute the specified method.
200      */

201     protected void checkAuthorization(Method JavaDoc method) throws org.openejb.OpenEJBException{
202         Object JavaDoc caller = getThreadSpecificSecurityIdentity();
203         boolean authorized = OpenEJB.getSecurityService().isCallerAuthorized(caller, deploymentInfo.getAuthorizedRoles(method));
204         if(!authorized)
205             throw new org.openejb.ApplicationException(new RemoteException JavaDoc("Unauthorized Access by Principal Denied"));
206     }
207
208     protected Object JavaDoc getThreadSpecificSecurityIdentity(){
209         ThreadContext context = ThreadContext.getThreadContext();
210         if(context.valid()){
211             return context.getSecurityIdentity();
212         }else{
213             return OpenEJB.getSecurityService().getSecurityIdentity();
214         }
215     }
216
217     /**
218       * This method enables/disables the copy process of the arguments and return value to and from
219      * a regular EJB invocation. In some cases it is desireable to skip the copy, e.g. when the
220      * invocation comes from an RMI or CORBA remote layer, where the arguemnts are already copies.
221      */

222     public void setIntraVmCopyMode(boolean on) {
223         doIntraVmCopy=on;
224     }
225         
226     /**
227      * Preserves the context of the current thread and passes the invoke on to the BaseEjbProxyHandler subclass where the Container will be asked to invoke the method on the bean.
228      *
229      * When entering a container the ThreadContext will change to match the context of
230      * the bean being serviced. That changes the current context of the calling bean,
231      * so the context must be preserved and then resourced after request is serviced.
232      * The context is restored after the subclass' _invoke method returns.
233      *
234      * @param proxy The Proxy object that represents this bean deployment's EJBObject or EJBHome.
235      * @param method The EJBHome or EJBObject method the caller is invoking.
236      * @param args The parameters to the mehtod being invoked
237      * @return The result of invoking the appropriate method on the bean instance.
238      * @exception Throwable
239      */

240     public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc{
241         if (isInvalidReference) throw new NoSuchObjectException JavaDoc("reference is invalid");
242
243
244         if (method.getDeclaringClass() == Object JavaDoc.class ) {
245             final String JavaDoc methodName = method.getName();
246
247             if ( methodName.equals( "toString" )) return toString();
248             else if (methodName.equals( "equals" )) return equals(args[0])?Boolean.TRUE: Boolean.FALSE;
249             else if (methodName.equals("hashCode")) return new Integer JavaDoc(hashCode());
250             else throw new UnsupportedOperationException JavaDoc("Unkown method: "+method);
251         } else if (method.getDeclaringClass() == IntraVmProxy.class ) {
252             final String JavaDoc methodName = method.getName();
253
254             if (methodName.equals("writeReplace")) return _writeReplace( proxy );
255             else throw new UnsupportedOperationException JavaDoc("Unkown method: "+method);
256         }
257         /* Preserve the context
258             When entering a container the ThreadContext will change to match the context of
259             the bean being serviced. That changes the current context of the calling bean,
260             so the context must be preserved and then resourced after request is serviced.
261             The context is restored in the finnaly clause below.
262             
263             We could have same some typing by obtaining a ref to the ThreadContext and then
264             setting the current ThreadContext to null, but this results in more object creation
265             since the container will create a new context on the invoke( ) operation if the current
266             context is null. Getting the context values and resetting them reduces object creation.
267             It's ugly but performant.
268         */

269         
270         ThreadContext cntext = null;
271         DeploymentInfo depInfo = null;
272         Object JavaDoc prmryKey = null;
273         byte crrntOperation = (byte)0;
274         Object JavaDoc scrtyIdentity = null;
275         boolean cntextValid = false;
276         cntext = ThreadContext.getThreadContext();
277         if(cntext.valid()){
278              depInfo = cntext.getDeploymentInfo();
279              prmryKey = cntext.getPrimaryKey();
280              crrntOperation = cntext.getCurrentOperation();
281              scrtyIdentity = cntext.getSecurityIdentity();
282              cntextValid = true;
283         }
284
285         String JavaDoc jndiEnc = System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
286 // System.setProperty(javax.naming.Context.URL_PKG_PREFIXES,"org.openejb.core.ivm.naming");
287
// the four operations on IntraVmCopyMonitor are quite expensive, because
288
// all of them require a Thread.currentThread() operation, which is native code
289
try{
290             if(doIntraVmCopy==true){// copy arguments as required by the specification
291
// demarcate the begining of the copy operation.
292
if(args!=null && args.length > 0) {
293                     // methods w/o arguments pass in a null value
294
IntraVmCopyMonitor.preCopyOperation();
295                     args = copyArgs(args);
296                     // demarcate end of copy operation
297
IntraVmCopyMonitor.postCopyOperation();
298                 }
299                 Object JavaDoc returnObj = _invoke(proxy,method,args);
300                 
301                 // demarcate the begining of the copy operation.
302
IntraVmCopyMonitor.preCopyOperation();
303                 returnObj = copyObj(returnObj);
304                 return returnObj;
305                 // postCopyOperation() is handled in try/finally clause.
306
} else {
307                 try {
308                     /*
309                     * The EJB 1.1 specification requires that arguments and return values between beans adhere to the
310                     * Java RMI copy semantics which requires that the all arguments be passed by value (copied) and
311                     * never passed as references. However, it is possible for the system administrator to turn off the
312                     * copy operation so that arguments and return values are passed by reference as a performance optimization.
313                     * Simply setting the org.openejb.core.EnvProps.INTRA_VM_COPY property to FALSE will cause
314                     * IntraVM to bypass the copy operations; arguments and return values will be passed by reference not value.
315                     * This property is, by default, always TRUE but it can be changed to FALSE by setting it as a System property
316                     * or a property of the Property argument when invoking OpenEJB.init(props). The doIntraVmCopy variable is set to that
317                     * property in the static block for this class.
318                     */

319                     
320                     return _invoke(proxy,method,args);
321                 } catch (RemoteException JavaDoc e) {
322                     if (this.isLocal()){
323                         throw new EJBException JavaDoc(e.getMessage()).initCause(e.getCause());
324                     } else {
325                         throw e;
326                     }
327                 } catch (Throwable JavaDoc t) {
328                     t.printStackTrace();
329                     Class JavaDoc[] etypes = method.getExceptionTypes();
330                     for (int i = 0; i < etypes.length; i++) {
331
332                         if (etypes[i].isAssignableFrom(t.getClass())){
333                             throw t;
334                         }
335                     }
336                     // Exception is undeclared
337
// Try and find a runtime exception in there
338
while (t.getCause() != null && !(t instanceof RuntimeException JavaDoc)){
339                         t = t.getCause();
340                     }
341                     throw t;
342                 }
343             }
344         } finally {
345 // System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, jndiEnc);
346
// restore the context
347
if(cntextValid){
348                 cntext.set(depInfo, prmryKey, scrtyIdentity);
349                 cntext.setCurrentOperation(crrntOperation);
350             }
351             if(doIntraVmCopy==true){
352                 // demarcate end of copy operation
353
// postCopyOperation() is executed here in case of an exception
354
IntraVmCopyMonitor.postCopyOperation();
355             }
356         }
357     }
358
359     public String JavaDoc toString() {
360         return "proxy="+getProxyInfo().getInterface().getName()+";deployment="+this.deploymentID+";pk="+this.primaryKey;
361     }
362     
363     public int hashCode() {
364         if(primaryKey==null) {
365             //stateless bean or home object
366
return deploymentID.hashCode();
367         }else {
368             return primaryKey.hashCode();
369         }
370     }
371     
372     public boolean equals(Object JavaDoc obj) {
373         try{
374             obj = ProxyManager.getInvocationHandler(obj);
375         }catch(IllegalArgumentException JavaDoc e) {
376             return false;
377         }
378         BaseEjbProxyHandler other = (BaseEjbProxyHandler) obj;
379         if(primaryKey==null) {
380             return other.primaryKey==null && deploymentID.equals(other.deploymentID);
381         } else {
382             return primaryKey.equals(other.primaryKey) && deploymentID.equals(other.deploymentID);
383         }
384     }
385
386     /**
387      * Overridden by subclasses and called by {@link #invoke}. Subclasses implement the main behavior of calling invoke on the Container that the bean deployment lives in.
388      *
389      * @param proxy The Proxy subclass that is the bean's EJBObject or EJBHome.
390      * @param method The bean method that the caller is attempting to invoke.
391      * @param args The arguments to the method being invoked.
392      * @return The result of invoking the appropriate method on the bean instance.
393      * @exception Throwable
394      */

395     protected abstract Object JavaDoc _invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc;
396
397
398     /**
399      *
400      * @param objects The object array you wish to dereference.
401      * @return An object array with new, equlivilent instances of the original objects contained in the array.
402      */

403
404     protected Object JavaDoc[] copyArgs(Object JavaDoc[] objects) throws IOException JavaDoc, ClassNotFoundException JavaDoc{
405         /*
406             while copying the arguments is necessary. Its not necessary to copy the array itself,
407             because they array is created by the Proxy implementation for the sole purpose of
408             packaging the arguments for the InvocationHandler.invoke( ) method. Its ephemeral
409             and their for doesn't need to be copied.
410         */

411
412         for (int i=0; i < objects.length; i++){
413             objects[i] = copyObj(objects[i]);
414         }
415
416         return objects;
417     }
418
419     /**
420      *
421      * @param object
422      * @return An equlivilent instance of the original object.
423      */

424     /* change dereference to copy */
425     protected Object JavaDoc copyObj(Object JavaDoc object) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
426         ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc(128);
427         ObjectOutputStream JavaDoc out = new ObjectOutputStream JavaDoc(baos);
428         out.writeObject(object);
429         out.close();
430
431         ByteArrayInputStream JavaDoc bais = new ByteArrayInputStream JavaDoc(baos.toByteArray());
432         ObjectInputStream JavaDoc in = new ObjectInputStream JavaDoc(bais);
433         Object JavaDoc obj = in.readObject();
434         return obj;
435     }
436     
437     /**
438     * Invalidates this reference so that it can not be used as a proxy for the bean identity. This
439     * method may be called when an InvalidateReferenceException is thrown by the container or when
440     * the bean identity associated with this proxy is explicitly removed, by calling one of the remove( )
441     * methods.
442     */

443     public void invalidateReference(){
444         this.container = null;
445         this.deploymentInfo = null;
446         this.isInvalidReference = true;
447     }
448
449     protected static void invalidateAllHandlers(Object JavaDoc key){
450         HashSet JavaDoc set = (HashSet JavaDoc)liveHandleRegistry.remove(key);
451         if(set==null)return;
452         synchronized(set){
453             Iterator JavaDoc handlers = set.iterator();
454             while(handlers.hasNext()){
455                 BaseEjbProxyHandler aHandler = (BaseEjbProxyHandler)handlers.next();
456                 aHandler.invalidateReference();
457             }
458         }
459     }
460     
461     protected abstract Object JavaDoc _writeReplace(Object JavaDoc proxy) throws ObjectStreamException JavaDoc;
462     
463     
464     protected static void registerHandler(Object JavaDoc key, BaseEjbProxyHandler handler){
465         HashSet JavaDoc set = (HashSet JavaDoc)liveHandleRegistry.get(key);
466         if(set!=null){
467             synchronized(set){
468                 set.add(handler);
469             }
470         }else{
471             set = new HashSet JavaDoc();
472             set.add(handler);
473             liveHandleRegistry.put(key, set);
474         }
475     }
476     
477     public abstract org.openejb.ProxyInfo getProxyInfo();
478     
479     public boolean isLocal() {
480         return isLocal;
481     }
482     public void setLocal(boolean isLocal) {
483         this.isLocal = isLocal;
484         this.doIntraVmCopy = !isLocal;
485     }
486 }
487
Popular Tags