001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.geronimo.corba;
018    
019    import java.lang.reflect.Method;
020    import java.rmi.AccessException;
021    import java.rmi.MarshalException;
022    import java.rmi.NoSuchObjectException;
023    import java.rmi.RemoteException;
024    import java.util.HashMap;
025    import java.util.Vector;
026    import java.util.Map;
027    
028    import javax.ejb.EJBHome;
029    import javax.ejb.EJBObject;
030    import javax.ejb.Handle;
031    import javax.ejb.RemoveException;
032    import javax.naming.Context;
033    import javax.naming.NamingException;
034    import javax.rmi.PortableRemoteObject;
035    import javax.transaction.InvalidTransactionException;
036    import javax.transaction.TransactionRequiredException;
037    import javax.transaction.TransactionRolledbackException;
038    
039    import org.apache.commons.logging.Log;
040    import org.apache.commons.logging.LogFactory;
041    import org.apache.geronimo.naming.enc.EnterpriseNamingContext;
042    import org.apache.geronimo.naming.java.RootContext;
043    import org.apache.geronimo.openejb.EjbDeployment;
044    import org.apache.openejb.InterfaceType;
045    import org.apache.openejb.BeanType;
046    import org.apache.openejb.RpcContainer;
047    import org.apache.openejb.OpenEJBException;
048    import org.apache.openejb.ProxyInfo;
049    import org.apache.geronimo.corba.util.Util;
050    import org.omg.CORBA.INVALID_TRANSACTION;
051    import org.omg.CORBA.MARSHAL;
052    import org.omg.CORBA.NO_PERMISSION;
053    import org.omg.CORBA.OBJECT_NOT_EXIST;
054    import org.omg.CORBA.ORB;
055    import org.omg.CORBA.SystemException;
056    import org.omg.CORBA.TRANSACTION_REQUIRED;
057    import org.omg.CORBA.TRANSACTION_ROLLEDBACK;
058    import org.omg.CORBA.UNKNOWN;
059    import org.omg.CORBA.portable.InputStream;
060    import org.omg.CORBA.portable.InvokeHandler;
061    import org.omg.CORBA.portable.OutputStream;
062    import org.omg.CORBA.portable.ResponseHandler;
063    import org.omg.CORBA.portable.UnknownException;
064    import org.omg.PortableServer.Servant;
065    
066    /**
067     * @version $Revision: 494431 $ $Date: 2007-01-09 07:18:14 -0800 (Tue, 09 Jan 2007) $
068     */
069    public class StandardServant extends Servant implements InvokeHandler {
070        private static final Log log = LogFactory.getLog(StandardServant.class);
071    
072        private static final Method GETEJBMETADATA = getMethod(EJBHome.class, "getEJBMetaData", null);
073        private static final Method GETHOMEHANDLE = getMethod(EJBHome.class, "getHomeHandle", null);
074        private static final Method REMOVE_W_KEY = getMethod(EJBHome.class, "remove", new Class[]{Object.class});
075        private static final Method REMOVE_W_HAND = getMethod(EJBHome.class, "remove", new Class[]{Handle.class});
076        private static final Method GETEJBHOME = getMethod(EJBObject.class, "getEJBHome", null);
077        private static final Method GETHANDLE = getMethod(EJBObject.class, "getHandle", null);
078        private static final Method GETPRIMARYKEY = getMethod(EJBObject.class, "getPrimaryKey", null);
079        private static final Method ISIDENTICAL = getMethod(EJBObject.class, "isIdentical", new Class[]{EJBObject.class});
080        private static final Method REMOVE = getMethod(EJBObject.class, "remove", null);
081    
082    
083        private final InterfaceType interfaceType;
084        private final EjbDeployment ejbDeployment;
085        private final Object primaryKey;
086        private final String[] typeIds;
087        private final Map operations;
088        private final Context enc;
089    
090        public StandardServant(ORB orb, InterfaceType ejbInterfaceType, EjbDeployment ejbDeployment) {
091            this(orb, ejbInterfaceType, ejbDeployment, null);
092        }
093    
094        public StandardServant(ORB orb, InterfaceType ejbInterfaceType, EjbDeployment ejbDeployment, Object primaryKey) {
095            this.interfaceType = ejbInterfaceType;
096            this.ejbDeployment = ejbDeployment;
097            this.primaryKey = primaryKey;
098    
099            // get the interface class
100            Class type;
101            if (InterfaceType.EJB_HOME == ejbInterfaceType) {
102                type = ejbDeployment.getHomeInterface();
103                if (type == null) {
104                    throw new IllegalArgumentException("EJB " + ejbDeployment.getEjbName() + " does not have a home interface");
105                }
106            } else if (InterfaceType.EJB_OBJECT == ejbInterfaceType) {
107                type = ejbDeployment.getRemoteInterface();
108                if (type == null) {
109                    throw new IllegalArgumentException("EJB " + ejbDeployment.getEjbName() + " does not have a remote interface");
110                }
111            } else {
112                throw new IllegalArgumentException("Only home and remote interfaces are supported in this servant: " + ejbInterfaceType);
113            }
114    
115            // build the operations index
116            this.operations = Util.mapOperationToMethod(type);
117    
118            // creat the corba ids array
119            typeIds = Util.createCorbaIds(type);
120    
121            // create ReadOnlyContext
122            Map componentContext = new HashMap(2);
123            componentContext.put("ORB", orb);
124            componentContext.put("HandleDelegate", new CORBAHandleDelegate());
125            try {
126                enc = EnterpriseNamingContext.createEnterpriseNamingContext(componentContext);
127            } catch (NamingException e) {
128                throw new RuntimeException("Error creating standard servant naming context", e);
129            }
130        }
131    
132        public InterfaceType getInterfaceType() {
133            return interfaceType;
134        }
135    
136        public EjbDeployment getEjbDeployment() {
137            return ejbDeployment;
138        }
139    
140        public Object getPrimaryKey() {
141            return primaryKey;
142        }
143    
144        public String[] _all_interfaces(org.omg.PortableServer.POA poa, byte[] objectId) {
145            return typeIds;
146        }
147    
148        public OutputStream _invoke(String operationName, InputStream _in, ResponseHandler reply) throws SystemException {
149            // get the method object
150            Method method = (Method) operations.get(operationName);
151    
152            org.omg.CORBA_2_3.portable.InputStream in = (org.omg.CORBA_2_3.portable.InputStream) _in;
153    
154            ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
155            Context oldContext = RootContext.getComponentContext();
156            try {
157                Thread.currentThread().setContextClassLoader(ejbDeployment.getClassLoader());
158                RootContext.setComponentContext(enc);
159    
160                // read in all of the arguments
161                Class[] parameterTypes = method.getParameterTypes();
162                Object[] arguments = new Object[parameterTypes.length];
163                for (int i = 0; i < parameterTypes.length; i++) {
164                    Class parameterType = parameterTypes[i];
165                    arguments[i] = Util.readObject(parameterType, in);
166                }
167    
168                // invoke the method
169                Object result = null;
170                try {
171    
172                    if (log.isDebugEnabled()) log.debug("Calling " + method.getName());
173    
174                    if (method.getDeclaringClass() == javax.ejb.EJBObject.class) {
175                        if (method.equals(GETHANDLE)) {
176                            result = ejbDeployment.getEjbObject(primaryKey).getHandle();
177                        } else if (method.equals(GETPRIMARYKEY)) {
178                            result = ejbDeployment.getEjbObject(primaryKey).getPrimaryKey();
179                        } else if (method.equals(ISIDENTICAL)) {
180                            org.omg.CORBA.Object thisObject = this._this_object();
181                            org.omg.CORBA.Object otherObject = (org.omg.CORBA.Object)arguments[0];
182                            result = new Boolean(thisObject._is_equivalent(otherObject));
183                        } else if (method.equals(GETEJBHOME)) {
184                            result = ejbDeployment.getEJBHome();
185                        } else if (method.equals(REMOVE)) {
186                            try {
187                                ejbDeployment.getEjbObject(primaryKey).remove();
188                                result = null;
189                            } catch (RemoveException e) {
190                                return Util.writeUserException(method, reply, e);
191                            }
192                        } else {
193                            throw new UnsupportedOperationException("unknown method: " + method);
194                        }
195                    } else if (method.getDeclaringClass() == javax.ejb.EJBHome.class) {
196                       if (method.equals(GETEJBMETADATA)) {
197                            result = ejbDeployment.getEJBHome().getEJBMetaData();
198                        } else if (method.equals(GETHOMEHANDLE)) {
199                            result = ejbDeployment.getEJBHome().getHomeHandle();
200                        } else if (method.equals(REMOVE_W_HAND)) {
201                            CORBAHandle handle = (CORBAHandle) arguments[0];
202                            try {
203                                if (ejbDeployment.getComponentType() == BeanType.STATELESS) {
204                                    if (handle == null) {
205                                        throw new RemoveException("Handle is null");
206                                    }
207                                    Class remoteInterface = ejbDeployment.getRemoteInterface();
208    
209    
210                                    try {
211                                        EJBObject narrowed = (EJBObject)PortableRemoteObject.narrow(handle.getEJBObject(), remoteInterface);
212                                        if (narrowed == null) {
213                                            throw new RemoteException("Handle does not hold a " + remoteInterface.getName());
214                                        }
215                                    } catch (ClassCastException e) {
216                                        throw new RemoteException("Handle does not hold a " + remoteInterface.getName(), e);
217                                    }
218                                } else {
219                                    try {
220                                        Object handleKey = handle.getPrimaryKey(); 
221                                        RpcContainer container = (RpcContainer) ejbDeployment.getContainer();
222                                        result = container.invoke(ejbDeployment.getDeploymentId(), method, arguments, handleKey, null);
223                                    } catch (OpenEJBException e) {
224                                        Throwable cause = e.getCause();
225                                        if (cause instanceof Exception) {
226                                            Exception exception = (Exception) cause;
227                                            return Util.writeUserException(method, reply, exception);
228                                        }
229                                        throw cause;
230                                    }
231                                }
232                            } catch (RemoveException e) {
233    
234                                return Util.writeUserException(method, reply, e);
235                            }
236                            result = null;
237                        } else if (method.equals(REMOVE_W_KEY)) {
238                            try {
239                                ejbDeployment.getEJBHome().remove(arguments[0]);
240                                result = null;
241                            } catch (RemoveException e) {
242                                return Util.writeUserException(method, reply, e);
243                            }
244                        } else {
245                            throw new UnsupportedOperationException("unknown method: " + method);
246                        }
247                    } else {
248                        try {
249                            RpcContainer container = (RpcContainer) ejbDeployment.getContainer();
250                            result = container.invoke(ejbDeployment.getDeploymentId(), method, arguments, primaryKey, null);
251                            // some methods like create() or find* return ProxyInfo objects.  We need to 
252                            // turn those into real EJB remote references. 
253                            if (result instanceof ProxyInfo || method.getName().startsWith("find")) {
254                                result = createProxy(result);                    
255                            }
256                        } catch (OpenEJBException e) {
257                            Throwable cause = e.getCause();
258                            if (cause instanceof Exception) {
259                                Exception exception = (Exception) cause;
260                                return Util.writeUserException(method, reply, exception);
261                            }
262                            throw cause;
263                        }
264                    }
265                } catch (TransactionRolledbackException e) {
266                    log.debug("TransactionRolledbackException", e);
267                    throw (SystemException)new TRANSACTION_ROLLEDBACK(e.toString()).initCause(e);
268                } catch (TransactionRequiredException e) {
269                    log.debug("TransactionRequiredException", e);
270                    throw (SystemException)new TRANSACTION_REQUIRED(e.toString()).initCause(e);
271                } catch (InvalidTransactionException e) {
272                    log.debug("InvalidTransactionException", e);
273                    throw (SystemException)new INVALID_TRANSACTION(e.toString()).initCause(e);
274                } catch (NoSuchObjectException e) {
275                    log.debug("NoSuchObjectException", e);
276                    throw (SystemException)new OBJECT_NOT_EXIST(e.toString()).initCause(e);
277                } catch (AccessException e) {
278                    log.debug("AccessException", e);
279                    throw (SystemException)new NO_PERMISSION(e.toString()).initCause(e);
280                } catch (MarshalException e) {
281                    log.debug("MarshalException", e);
282                    throw (SystemException)new MARSHAL(e.toString()).initCause(e);
283                } catch (RemoteException e) {
284                    log.debug("RemoteException", e);
285                    throw (SystemException)new UnknownException(e).initCause(e);
286                } catch (RuntimeException e) {
287                    log.debug("RuntimeException", e);
288                    RemoteException remoteException = new RemoteException(e.getClass().getName() + " thrown from " + ejbDeployment.getDeploymentId() + ": " + e.getMessage(), e);
289                    throw new UnknownException(remoteException);
290                } catch (Error e) {
291                    log.debug("Error", e);
292                    RemoteException remoteException = new RemoteException(e.getClass().getName() + " thrown from " + ejbDeployment.getDeploymentId() + ": " + e.getMessage(), e);
293                    throw new UnknownException(remoteException);
294                } catch (Throwable e) {
295                    log.warn("Unexpected throwable", e);
296                    throw (SystemException)new UNKNOWN("Unknown exception type " + e.getClass().getName() + ": " + e.getMessage()).initCause(e);
297                }
298    
299                // creat the output stream
300                org.omg.CORBA_2_3.portable.OutputStream out = (org.omg.CORBA_2_3.portable.OutputStream) reply.createReply();
301    
302                // write the output value
303                Util.writeObject(method.getReturnType(), result, out);
304    
305                return out;
306            } finally {
307                Thread.currentThread().setContextClassLoader(oldClassLoader);
308                RootContext.setComponentContext(oldContext);
309            }
310        }
311    
312        private static Method getMethod(Class c, String method, Class[] params) {
313            try {
314                return c.getMethod(method, params);
315            } catch (NoSuchMethodException e) {
316                throw (IllegalStateException) new IllegalStateException().initCause(e);
317            }
318        }
319    
320        /**
321         * Convert ProxyInfo items in a create* or find* result
322         * for returning as a corba result. 
323         * 
324         * @param retValue The return value.
325         * 
326         * @return A CORBA compatible return result. 
327         * @exception Throwable
328         */
329        protected Object createProxy(Object retValue) throws Throwable {
330            if (retValue instanceof java.util.Collection) {
331                Object [] proxyInfos = ((java.util.Collection) retValue).toArray();
332                Vector proxies = new Vector();
333                for (int i = 0; i < proxyInfos.length; i++) {
334                    ProxyInfo proxyInfo = (ProxyInfo) proxyInfos[i];
335                    proxies.addElement(Util.getEJBProxy(proxyInfo));
336                }
337                return proxies;
338            } else if (retValue instanceof org.apache.openejb.util.ArrayEnumeration) {
339                org.apache.openejb.util.ArrayEnumeration enumeration = (org.apache.openejb.util.ArrayEnumeration) retValue;
340                for (int i = enumeration.size() - 1; i >= 0; --i) {
341                    ProxyInfo proxyInfo = ((ProxyInfo) enumeration.get(i));
342                    enumeration.set(i, Util.getEJBProxy(proxyInfo));
343                }
344                return enumeration;
345            } else if (retValue instanceof java.util.Enumeration) {
346                java.util.Enumeration enumeration = (java.util.Enumeration) retValue;
347    
348                java.util.List proxies = new java.util.ArrayList();
349                while (enumeration.hasMoreElements()) {
350                    ProxyInfo proxyInfo = ((ProxyInfo) enumeration.nextElement());
351                    proxies.add(Util.getEJBProxy(proxyInfo));
352                }
353                return new org.apache.openejb.util.ArrayEnumeration(proxies);
354            } else {
355                org.apache.openejb.ProxyInfo proxyInfo = (org.apache.openejb.ProxyInfo) retValue;
356                return Util.getEJBProxy(proxyInfo);
357            }
358    
359        }
360    }