001: /**
002: * EasyBeans
003: * Copyright (C) 2006-2007 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: LocalCallInvocationHandler.java 2010 2007-10-26 13:19:08Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.proxy.client;
025:
026: import java.io.Externalizable;
027: import java.io.IOException;
028: import java.io.ObjectInput;
029: import java.io.ObjectOutput;
030: import java.lang.reflect.Method;
031: import java.util.HashMap;
032:
033: import javax.ejb.EJBLocalObject;
034: import javax.ejb.NoSuchEJBException;
035: import javax.ejb.NoSuchObjectLocalException;
036:
037: import org.ow2.easybeans.api.EZBContainer;
038: import org.ow2.easybeans.api.EZBServer;
039: import org.ow2.easybeans.api.EmbeddedManager;
040: import org.ow2.easybeans.api.Factory;
041: import org.ow2.easybeans.rpc.api.EJBResponse;
042: import org.ow2.easybeans.rpc.api.RPCException;
043: import org.ow2.easybeans.rpc.util.Hash;
044:
045: /**
046: * Object acting as the proxy for local interfaces calls.
047: * @author Florent Benoit
048: */
049: public class LocalCallInvocationHandler extends AbsInvocationHandler
050: implements Externalizable {
051:
052: /**
053: * UID for serialization.
054: */
055: private static final long serialVersionUID = -4327634481654235615L;
056:
057: /**
058: * Embedded server ID.
059: */
060: private Integer embeddedID = null;
061:
062: /**
063: * Factory for sending requests (build in constructor or when serialization
064: * occurs).
065: */
066: private transient Factory<?, ?> factory = null;
067:
068: /**
069: * Boolean used to know if this class extends javax.ejb.EJBLocalObject class.
070: */
071: private boolean isExtendingEJBLocalObject = false;
072:
073: /**
074: * Build a new Invocation handler.
075: * @param embeddedID the Embedded server ID.
076: * @param containerId the id of the container that will be called on the
077: * remote side.
078: * @param factoryName the name of the remote factory.
079: * @param useID true if all instance build with this ref are unique
080: * (stateful), false if it references the same object (stateless)
081: */
082: public LocalCallInvocationHandler(final Integer embeddedID,
083: final String containerId, final String factoryName,
084: final boolean useID) {
085: super (containerId, factoryName, useID);
086:
087: // Server ID
088: this .embeddedID = embeddedID;
089:
090: // Init the factory
091: initFactory();
092: }
093:
094: /**
095: * Default constructor (used for serialization).
096: */
097: public LocalCallInvocationHandler() {
098: super (null, null, false);
099: }
100:
101: /**
102: * Initialize the factory object with the given infos.
103: */
104: private void initFactory() {
105: // Get Embedded server
106: EZBServer ejb3Server = EmbeddedManager.getEmbedded(embeddedID);
107: if (ejb3Server == null) {
108: throw new IllegalStateException(
109: "Cannot find the server with id '" + embeddedID
110: + "'.");
111: }
112:
113: // Get the container
114: EZBContainer container = ejb3Server
115: .getContainer(getContainerId());
116: if (container == null) {
117: throw new IllegalStateException(
118: "Cannot find the container with id '"
119: + getContainerId() + "'.");
120: }
121:
122: factory = container.getFactory(getFactoryName());
123: if (factory == null) {
124: throw new IllegalStateException(
125: "Cannot find the factory with name '"
126: + getFactoryName() + "'.");
127: }
128: }
129:
130: /**
131: * Processes a method invocation on a proxy instance and returns the result.
132: * This method will be invoked on an invocation handler when a method is
133: * invoked on a proxy instance that it is associated with.
134: * @param proxy the proxy instance that the method was invoked on
135: * @param method the <code>Method</code> instance corresponding to the
136: * interface method invoked on the proxy instance. The declaring
137: * class of the <code>Method</code> object will be the interface
138: * that the method was declared in, which may be a superinterface of
139: * the proxy interface that the proxy class inherits the method
140: * through.
141: * @param args an array of objects containing the values of the arguments
142: * passed in the method invocation on the proxy instance, or
143: * <code>null</code> if interface method takes no arguments.
144: * Arguments of primitive types are wrapped in instances of the
145: * appropriate primitive wrapper class, such as
146: * <code>java.lang.Integer</code> or <code>java.lang.Boolean</code>.
147: * @return the value to return from the method invocation on the proxy
148: * instance.
149: * @throws Exception the exception to throw from the method invocation on
150: * the proxy instance.
151: */
152: public Object invoke(final Object proxy, final Method method,
153: final Object[] args) throws Exception {
154: // bean removed ?
155: if (isRemoved()) {
156: handleThrowable(convertThrowable(new NoSuchEJBException(
157: "The bean has been removed")), false, method, null);
158: }
159:
160: // Methods on the Object.class are not send on the remote side
161: if (method.getDeclaringClass().getName().equals(
162: "java.lang.Object")) {
163: // for stateful bean, let the first call to toString go to the remote side in order to initialize the bean ID
164: if (!isUsingID() || getBeanId() != null
165: || !method.getName().equals("toString")) {
166: return handleObjectMethods(method, args);
167: }
168: }
169:
170: if (getHashedMethods() == null) {
171: setHashedMethods(new HashMap<Method, Long>());
172: }
173:
174: Long hashLong = getHashedMethods().get(method);
175: if (hashLong == null) {
176: hashLong = Long.valueOf(Hash.hashMethod(method));
177: getHashedMethods().put(method, hashLong);
178: }
179:
180: long hash = hashLong.longValue();
181:
182: // Now, need to invoke the bean
183: EJBResponse response = null;
184: response = factory.localCall(hash, args, getBeanId());
185: setBeanId(response.getBeanId());
186:
187: // bean removed ?
188: setRemoved(response.isRemoved());
189:
190: RPCException rpcException = response.getRPCException();
191: if (rpcException != null) {
192: handleThrowable(rpcException.getCause(), rpcException
193: .isApplicationException(), method, rpcException);
194: }
195:
196: return response.getValue();
197:
198: }
199:
200: /**
201: * Save our content.
202: * @param out the stream to write the object to
203: * @throws IOException Includes any I/O exceptions that may occur
204: */
205: public void writeExternal(final ObjectOutput out)
206: throws IOException {
207: // server ID
208: out.writeObject(embeddedID);
209: // Container ID
210: out.writeObject(getContainerId());
211: // Factory name
212: out.writeObject(getFactoryName());
213: // interface class name
214: out.writeObject(getInterfaceClassName());
215: // boolean (extending java.rmi.remote ?)
216: out.writeBoolean(isExtendingRmiRemote());
217: // boolean (extending javax.ejb.EJBLocalObject ?)
218: out.writeBoolean(isExtendingEJBLocalObject);
219:
220: // boolean (useID flag)
221: out.writeBoolean(isUsingID());
222:
223: // boolean (removed)
224: out.writeBoolean(isRemoved());
225: }
226:
227: /**
228: * Build our content.
229: * @param in the stream to read data from in order to restore the object
230: * @exception IOException if I/O errors occur
231: * @exception ClassNotFoundException If the class for an object being
232: * restored cannot be found.
233: */
234: public void readExternal(final ObjectInput in) throws IOException,
235: ClassNotFoundException {
236: // read ServerID
237: this .embeddedID = (Integer) in.readObject();
238: // read Container id
239: setContainerId((String) in.readObject());
240: // read factory's name
241: setFactoryName((String) in.readObject());
242:
243: // interface class name
244: setInterfaceClassName((String) in.readObject());
245:
246: // boolean flag
247: setExtendingRmiRemote(in.readBoolean());
248:
249: // boolean (extending javax.ejb.EJBLocalObject ?)
250: isExtendingEJBLocalObject = (in.readBoolean());
251:
252: // useID flag
253: setUseID(in.readBoolean());
254:
255: // removed flag
256: setRemoved(in.readBoolean());
257:
258: // init Factory object (transient)
259: initFactory();
260:
261: }
262:
263: /**
264: * Gets the embedded ID.
265: * @return the embedded ID.
266: */
267: protected Integer getEmbeddedID() {
268: return embeddedID;
269: }
270:
271: /**
272: * Sets the interface that represents this handler.
273: * @param clz the instance of the interface.
274: */
275: @Override
276: public void setInterfaceClass(final Class<?> clz) {
277: super .setInterfaceClass(clz);
278: if (EJBLocalObject.class.isAssignableFrom(clz)) {
279: isExtendingEJBLocalObject = true;
280: }
281: }
282:
283: /**
284: * Convert the received exception to the correct type for the remote case.
285: * @param throwable the exception to analyze.
286: * @return the converted exception or the original exception
287: */
288: protected Throwable convertThrowable(final Throwable throwable) {
289: // see 14.4.2.3 chapter EJB3
290: if (isExtendingEJBLocalObject
291: && throwable instanceof NoSuchEJBException) {
292: NoSuchObjectLocalException ne = new NoSuchObjectLocalException(
293: throwable.getMessage(), (Exception) throwable);
294: return ne;
295: }
296: // no check
297: return throwable;
298: }
299: }
|