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: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Mikhail A. Markov
021: * @version $Revision: 1.1.2.2 $
022: */package org.apache.harmony.rmi.remoteref;
023:
024: import java.io.IOException;
025: import java.io.ObjectInput;
026: import java.io.ObjectOutput;
027: import java.lang.reflect.AccessibleObject;
028: import java.lang.reflect.InvocationHandler;
029: import java.lang.reflect.InvocationTargetException;
030: import java.lang.reflect.Method;
031: import java.lang.reflect.Proxy;
032: import java.net.BindException;
033: import java.rmi.Remote;
034: import java.rmi.RemoteException;
035: import java.rmi.ServerError;
036: import java.rmi.ServerException;
037: import java.rmi.StubNotFoundException;
038: import java.rmi.UnmarshalException;
039: import java.rmi.server.ExportException;
040: import java.rmi.server.ObjID;
041: import java.rmi.server.RMIClientSocketFactory;
042: import java.rmi.server.RMIServerSocketFactory;
043: import java.rmi.server.RemoteCall;
044: import java.rmi.server.RemoteObjectInvocationHandler;
045: import java.rmi.server.RemoteRef;
046: import java.rmi.server.RemoteStub;
047: import java.rmi.server.ServerNotActiveException;
048: import java.rmi.server.ServerRef;
049: import java.rmi.server.Skeleton;
050: import java.security.AccessController;
051: import java.security.PrivilegedAction;
052: import java.util.HashMap;
053: import java.util.Map;
054:
055: import org.apache.harmony.rmi.common.GetBooleanPropAction;
056: import org.apache.harmony.rmi.common.RMILog;
057: import org.apache.harmony.rmi.common.RMIProperties;
058: import org.apache.harmony.rmi.common.RMIUtil;
059: import org.apache.harmony.rmi.internal.nls.Messages;
060: import org.apache.harmony.rmi.server.RMIReference;
061: import org.apache.harmony.rmi.server.ServerConnectionManager;
062: import org.apache.harmony.rmi.transport.Endpoint;
063: import org.apache.harmony.rmi.transport.RMIObjectInputStream;
064: import org.apache.harmony.rmi.transport.RMIObjectOutputStream;
065:
066: /**
067: * Implementation of server-side handle for remote objects.
068: *
069: * @author Mikhail A. Markov
070: * @version $Revision: 1.1.2.2 $
071: */
072: public class UnicastServerRef extends UnicastRef implements ServerRef {
073:
074: private static final long serialVersionUID = 3913676048828136951L;
075:
076: /** Implementation which this handle refers to. */
077: protected RMIReference ref = null;
078:
079: /**
080: * Skeleton for this remote object if we use RMI protocol 1.1 or null
081: * if we use RMI protocol 1.2.
082: */
083: protected Skeleton skel = null;
084:
085: /**
086: * Map with remote methods.
087: * Methods hash codes are the keys in the table.
088: */
089: protected Map remoteMethods = new HashMap();
090:
091: /** True if the handled remote object is system. */
092: protected boolean isSystem;
093:
094: /**
095: * ServerConnectionManager accepting connections for this ServerRef.
096: */
097: protected ServerConnectionManager mgr;
098:
099: // The name of Remote class for login purposes.
100: private String implClassName = null;
101:
102: // Log where to write server-side log of remote calls.
103: private static final RMILog serverCallsLog = RMILog
104: .getServerCallsLog();
105:
106: // Log where to write server-side log of remote reference activity
107: private static final RMILog serverRefLog = RMILog.getServerRefLog();
108:
109: // Should we suppress stack traces before sending to client or not
110: private static final boolean suppressST = ((Boolean) AccessController
111: .doPrivileged(new GetBooleanPropAction(
112: RMIProperties.SUPPRESSSTACKTRACES_PROP)))
113: .booleanValue();
114:
115: // Should we print on server side stack traces or not
116: private static final boolean printST = ((Boolean) AccessController
117: .doPrivileged(new GetBooleanPropAction(
118: RMIProperties.EXCEPTIONTRACE_PROP))).booleanValue();
119:
120: // Should we generate dynamic proxy stubs only or not.
121: private static final boolean ignoreStubClasses = ((Boolean) AccessController
122: .doPrivileged(new GetBooleanPropAction(
123: RMIProperties.IGNORESTUBCLASSES_PROP)))
124: .booleanValue();
125:
126: /**
127: * Constructs default UnicastServerRef listening on anonymous port.
128: */
129: public UnicastServerRef() {
130: this (0);
131: }
132:
133: /**
134: * Constructs UnicastServerRef listening on the port specified.
135: *
136: * @param port port where this UnicastServerRef will listen for connections
137: */
138: public UnicastServerRef(int port) {
139: this (port, null, null, new ObjID());
140: }
141:
142: /**
143: * Constructs UnicastServerRef listening on the port specified and
144: * having the given client and server socket factories.
145: *
146: * @param port port where this UnicastServerRef will listen for connections
147: * @param csf client-side socket factory for creating client sockets
148: * @param ssf server-side socket factory for creating server sockets
149: */
150: public UnicastServerRef(int port, RMIClientSocketFactory csf,
151: RMIServerSocketFactory ssf) {
152: this (port, csf, ssf, new ObjID());
153: }
154:
155: /**
156: * Constructs UnicastServerRef listening on the port specified,
157: * using specified client and server socket factories and
158: * having the given ObjID.
159: *
160: * @param port port where this UnicastServerRef will listen for connections
161: * @param csf client-side socket factory for creating client sockets
162: * @param ssf server-side socket factory for creating server sockets
163: * @param objId Object ID of remote object
164: */
165: public UnicastServerRef(int port, RMIClientSocketFactory csf,
166: RMIServerSocketFactory ssf, ObjID objId) {
167: super ();
168: isLocal = true;
169: ep = new Endpoint(port, csf, ssf);
170: this .objId = objId;
171: }
172:
173: /**
174: * Constructs UnicastServerRef using specified Endpoint and ObjID.
175: *
176: * @param ep Endpoint for remote calls
177: * @param objId Object ID of remote object
178: */
179: public UnicastServerRef(Endpoint ep, ObjID objId) {
180: super (ep, objId);
181: }
182:
183: /**
184: * @see ServerRef.exportObject(Remote, Object)
185: */
186: public RemoteStub exportObject(Remote obj, Object data)
187: throws RemoteException {
188: return (RemoteStub) exportObject(obj, data, false, true, false);
189: }
190:
191: /**
192: * @see ServerRef.getClientHost()
193: */
194: public String getClientHost() throws ServerNotActiveException {
195: String host = ServerConnectionManager.getClientHost();
196:
197: if (host == null) {
198: // rmi.5B=There are no in-progress RMI calls in the current thread.
199: throw new ServerNotActiveException(Messages
200: .getString("rmi.5B")); //$NON-NLS-1$
201: }
202: return host;
203: }
204:
205: /**
206: * @see RemoteRef.getRefClass(ObjectOutput)
207: */
208: public String getRefClass(ObjectOutput out) {
209: return "UnicastServerRef"; //$NON-NLS-1$
210: }
211:
212: /**
213: * For this type of ref no additional data is written to the stream.
214: */
215: public void writeExternal(ObjectOutput out) throws IOException {
216: }
217:
218: /**
219: * For this type of ref no additional data is read from the stream.
220: */
221: public void readExternal(ObjectInput in) throws IOException,
222: ClassNotFoundException {
223: }
224:
225: /**
226: * Exports remote object so it becomes available for remote calls.
227: *
228: * @param obj remote object implementation
229: * @param data additional data needed for exporting the object (not used)
230: * @param useProxyStubs If true then Proxy stubs will be generated if stub
231: * class could not be found in classpath and codebase; if false Proxy
232: * stubs will not be tried (this is needed for
233: * UnicastRemoteObject.exportObject(Remote) method because it
234: * returns RemoteStub class (but Proxy class could not be casted
235: * to it)
236: * @param startListen if false, ServerSocket listening thread will not be
237: * started (this is used for DGC, for example); otherwise listening
238: * thread will be started and object becomes available for
239: * connections from clients
240: * @param isSystem if true then existence of this object will not prevent
241: * VM from exiting (for example, for rmiregistry)
242: *
243: * @throws RemoteException if any exception occurred while trying to export
244: * the object
245: */
246: public Remote exportObject(Remote obj, Object data,
247: boolean useProxyStubs, boolean startListen, boolean isSystem)
248: throws RemoteException {
249: this .isSystem = isSystem;
250: ref = new RMIReference(obj);
251: implClassName = obj.getClass().getName();
252: Remote stub = null;
253:
254: // obtain class directly implementing Remote interface
255: Class remoteClass = RMIUtil.getRemoteClass(obj.getClass());
256:
257: // load and instantiate skel class if any
258: skel = getSkelInstance(remoteClass);
259: boolean isProxyStub = false;
260:
261: if (serverRefLog.isLoggable(RMILog.VERBOSE)) {
262: // rmi.log.95=Obtaining stub class for: {0}
263: serverRefLog.log(RMILog.VERBOSE, Messages.getString(
264: "rmi.log.95", //$NON-NLS-1$
265: remoteClass.getName()));
266: }
267: Class stubClass = null;
268:
269: if (!useProxyStubs || !ignoreStubClasses) {
270: // load stub class statically generated by rmic
271: stubClass = loadStubClass(remoteClass, !useProxyStubs);
272:
273: // rmi.log.96=Loaded "static" stub for {0}
274: if (serverRefLog.isLoggable(RMILog.VERBOSE)) {
275: serverRefLog.log(RMILog.VERBOSE, Messages.getString(
276: "rmi.log.96", //$NON-NLS-1$
277: remoteClass.getName()));
278: }
279: }
280:
281: if (stubClass == null) {
282: // try to create dynamic proxy stub class
283: try {
284: stubClass = Proxy.getProxyClass(obj.getClass()
285: .getClassLoader(), RMIUtil
286: .getRemoteInterfaces(obj.getClass()));
287: //stubClass = RMIClassLoader.loadProxyClass(null,
288: // RMIUtil.getRemoteInterfacesNames(obj.getClass()),
289: // obj.getClass().getClassLoader());
290:
291: if (serverRefLog.isLoggable(RMILog.VERBOSE)) {
292: // rmi.log.97=Loaded dynamic stub for {0}
293: serverRefLog.log(RMILog.VERBOSE, Messages
294: .getString("rmi.log.97", //$NON-NLS-1$
295: remoteClass.getName()));
296: }
297: isProxyStub = true;
298: } catch (Exception ex) {
299: // rmi.62=Unable to create dynamic proxy stub class
300: throw new StubNotFoundException(Messages
301: .getString("rmi.62"), ex); //$NON-NLS-1$
302: }
303: }
304:
305: try {
306:
307: // add remote method which could be invoked to the hash table
308: remoteMethods = RMIUtil.getRemoteMethods(obj.getClass());
309:
310: /*
311: * Make all remote methods accessible (for protected or
312: * package-protected cases).
313: */
314: AccessController.doPrivileged(new PrivilegedAction() {
315: public Object run() {
316: AccessibleObject.setAccessible(
317: (Method[]) (remoteMethods.values()
318: .toArray(new Method[remoteMethods
319: .size()])), true);
320: return null;
321: }
322: });
323:
324: // init stub
325: UnicastRef rref = getClientRef(ep, objId);
326:
327: if (!isProxyStub) {
328: stub = (RemoteStub) stubClass.getConstructor(
329: new Class[] { RemoteRef.class }).newInstance(
330: new Object[] { rref });
331: } else {
332: stub = (Remote) stubClass
333: .getConstructor(
334: new Class[] { InvocationHandler.class })
335: .newInstance(
336: new Object[] { new RemoteObjectInvocationHandler(
337: rref) });
338: }
339:
340: if (serverRefLog.isLoggable(RMILog.VERBOSE)) {
341: // rmi.log.98=Instantiated stub: {0}
342: serverRefLog.log(RMILog.VERBOSE, Messages.getString(
343: "rmi.log.98", stub)); //$NON-NLS-1$
344: }
345:
346: if (startListen) {
347: // start listening thread
348: mgr = ServerConnectionManager.getMgr(ep);
349: }
350: } catch (BindException be) {
351: // rmi.63=Unable to export object: port {0} already in use
352: throw new ExportException(Messages.getString("rmi.63", //$NON-NLS-1$
353: ep.getPort()), be);
354: } catch (Exception ex) {
355: // rmi.64=Unable to export object on port {0}
356: throw new ExportException(Messages.getString("rmi.64", //$NON-NLS-1$
357: ep.getPort()), ex);
358: }
359: return stub;
360: }
361:
362: /**
363: * Returns true if force parameter is false and there are no in-progress
364: * calls to the object handled by this ref and false otherwise. This method
365: * could be overridden by subclasses to "really" unexport handled object.
366: *
367: * @param force if true then we may not care about active calls
368: *
369: * @return true if force parameter is false and there are no in-progress
370: * calls to the object handled by this ref and false otherwise
371: */
372: public boolean unexportObject(boolean force) {
373: return force ? true : !mgr.hasActiveCalls();
374: }
375:
376: /**
377: * Returns true if the handled Remote object is system and false otherwise.
378: *
379: * @return true if the handled Remote object is system and false otherwise
380: */
381: public boolean isSystem() {
382: return isSystem;
383: }
384:
385: /**
386: * Performs actual remote method invocation.
387: *
388: * @param call RemoteCall
389: *
390: * @throws IOException if any I/O error occurred during remote method call
391: */
392: public void processCall(RemoteCall call) throws IOException {
393: // read method and parameters
394: RMIObjectInputStream oin = (RMIObjectInputStream) call
395: .getInputStream();
396: int op = oin.readInt(); // read operation
397: long h = oin.readLong(); // read method hash
398:
399: if (op != -1) {
400: // Using 1.1. RMI protocol version
401: if (skel == null) {
402: // rmi.65=Skeleton class not found.
403: throw new UnmarshalException(Messages
404: .getString("rmi.65")); //$NON-NLS-1$
405: }
406: String m = skel.getOperations()[op].toString();
407: logServerCall(m);
408:
409: try {
410: skel.dispatch((Remote) ref.get(), call, op, h);
411: } catch (Throwable t) {
412: Exception ex = prepareException(m, t);
413: RMIObjectOutputStream oout = (RMIObjectOutputStream) call
414: .getResultStream(false);
415: oout.writeObject(ex);
416: }
417:
418: try {
419: call.getOutputStream().flush();
420: } catch (IOException ioe) {
421: }
422: return;
423: }
424:
425: // Using 1.2 RMI protocol version
426: Method m = (Method) remoteMethods.get(new Long(h));
427:
428: if (m == null) {
429: // rmi.66=Method with hash = {0} not found.
430: throw new UnmarshalException(Messages
431: .getString("rmi.66", h)); //$NON-NLS-1$
432: }
433: logServerCall(m.toString());
434: Object[] params = readParams(m, oin);
435: call.releaseInputStream();
436: Object toReturn = null;
437: Throwable toThrow = null;
438:
439: // locally call the method
440: try {
441: toReturn = m.invoke(ref.get(), params);
442: } catch (InvocationTargetException ite) {
443: toThrow = prepareException(m.toString(),
444: ((InvocationTargetException) ite)
445: .getTargetException());
446: } catch (Throwable t) {
447: toThrow = prepareException(m.toString(), t);
448: }
449:
450: // return result of method call
451: RMIObjectOutputStream oout = (RMIObjectOutputStream) call
452: .getResultStream(toThrow == null);
453:
454: try {
455: if (toThrow != null) {
456: oout.writeObject(toThrow);
457: } else if (toReturn != null) {
458: oout.writeRMIObject(toReturn, m.getReturnType());
459: } else if (m.getReturnType() != Void.TYPE) {
460: oout.writeObject(null);
461: }
462: oout.flush();
463: } catch (Error er) {
464: // rmi.67=Error occurred while marshalling return value
465: throw new ServerError(Messages.getString("rmi.67"), er); //$NON-NLS-1$
466: }
467: }
468:
469: /**
470: * Loads stub class for the given remote class.
471: *
472: * @param c Class whose stub should be loaded
473: * @param throwException should we throw StubNotFoundException in case of
474: * failure or silently return null
475: *
476: * @return loaded stub or null if throwException is false and any failure
477: * occurred during stub loading
478: *
479: * @throws StubNotFoundException if throwException parameter is true and any
480: * failure occurred during stub loading
481: */
482: protected Class loadStubClass(Class c, boolean throwException)
483: throws StubNotFoundException {
484: String stubName = c.getName() + "_Stub"; //$NON-NLS-1$
485: ClassLoader cl = c.getClassLoader();
486:
487: try {
488: if (cl != null) {
489: return cl.loadClass(stubName);
490: } else {
491: return Class.forName(stubName);
492: }
493: } catch (ClassNotFoundException cnfe) {
494: if (throwException) {
495: // rmi.68=Stub {0} not found.
496: throw new StubNotFoundException(Messages.getString(
497: "rmi.68", stubName), cnfe); //$NON-NLS-1$
498: }
499: }
500: return null;
501: }
502:
503: /**
504: * Loads and instantiates skel class for the given remote class.
505: *
506: * @param c Class whose skel should be loaded and instantiated
507: *
508: * @return created skel class or null if any Exception occurred during
509: * skel loading or instantiating
510: */
511: protected Skeleton getSkelInstance(Class c) {
512: String skelName = c.getName() + "_Skel"; //$NON-NLS-1$
513: ClassLoader cl = c.getClassLoader();
514:
515: try {
516: Class skelClass;
517:
518: if (cl != null) {
519: skelClass = cl.loadClass(skelName);
520: } else {
521: skelClass = Class.forName(skelName);
522: }
523: return (Skeleton) skelClass.newInstance();
524: } catch (Exception ex) {
525: }
526: return null;
527: }
528:
529: /**
530: * Creates client-side reference holding the given Endpoint and Object ID.
531: *
532: * @param ep Endpoint for UnicastRef creation
533: * @param objId Object ID for UnicastRef creation
534: *
535: * @return created client-sice reference
536: */
537: protected UnicastRef getClientRef(Endpoint ep, ObjID objId) {
538: if (ep.getClientSocketFactory() == null) {
539: return new UnicastRef(ep, objId, true);
540: } else {
541: return new UnicastRef2(ep, objId, true);
542: }
543: }
544:
545: // Reads parameters for the given method from the specified InputStream.
546: private Object[] readParams(Method m, RMIObjectInputStream oin)
547: throws RemoteException {
548: Class[] paramTypes = m.getParameterTypes();
549: Object[] params = new Object[paramTypes.length];
550:
551: try {
552: for (int i = 0; i < paramTypes.length; ++i) {
553: params[i] = oin.readRMIObject(paramTypes[i]);
554: }
555: } catch (RemoteException re) {
556: // rmi.69=RemoteException occurred while unmarshalling arguments
557: throw new ServerException(Messages.getString("rmi.69"), re); //$NON-NLS-1$
558: } catch (IOException ioe) {
559: // rmi.6A=IOException occurred while unmarshalling arguments
560: throw new UnmarshalException(
561: Messages.getString("rmi.6A"), ioe); //$NON-NLS-1$
562: } catch (ClassNotFoundException cnfe) {
563: // rmi.6B=ClassNotFoundException occurred while unmarshalling arguments
564: throw new UnmarshalException(
565: Messages.getString("rmi.6B"), cnfe); //$NON-NLS-1$
566: } catch (Error er) {
567: // rmi.6C=Error occurred while unmarshalling arguments
568: throw new ServerError(Messages.getString("rmi.6C"), er); //$NON-NLS-1$
569: }
570: return params;
571: }
572:
573: /*
574: * Prepares Exception to be sent to client as a result of remote method
575: * call.
576: *
577: * @param m Method string representation which cause the exception
578: * @param t Throwable to be processed.
579: *
580: * @return prepared Exception
581: */
582: private Exception prepareException(String m, Throwable t) {
583: Throwable preparedEx = null;
584: logServerException(m, t);
585:
586: if (t instanceof Error) {
587: // rmi.6D=Error occurred while remote method invocation
588: preparedEx = new ServerError(
589: Messages.getString("rmi.6D"), (Error) t); //$NON-NLS-1$
590: } else if (t instanceof RemoteException) {
591: // rmi.6E=RemoteException occurred while remote method invocation
592: preparedEx = new ServerException(Messages
593: .getString("rmi.6E"), //$NON-NLS-1$
594: (RemoteException) t);
595: } else {
596: preparedEx = t;
597: }
598: Exception toReturn = (Exception) preparedEx;
599:
600: if (suppressST) {
601: // clear stack traces
602: StackTraceElement[] emptyST = new StackTraceElement[0];
603:
604: for (; preparedEx != null; preparedEx = preparedEx
605: .getCause()) {
606: preparedEx.setStackTrace(emptyST);
607: }
608: }
609: return toReturn;
610: }
611:
612: // Logs remote method call.
613: private void logServerCall(String m) {
614: if (serverCallsLog.isLoggable(RMILog.VERBOSE)) {
615: String client = ServerConnectionManager.getClientHost();
616:
617: if (client != null) {
618: // rmi.log.99=Remote call from [{0}]
619: client = Messages.getString("rmi.log.99", client); //$NON-NLS-1$ //$NON-NLS-2$
620: } else {
621: // rmi.log.9A=Local remote call
622: client = Messages.getString("rmi.log.9A"); //$NON-NLS-1$
623: }
624: // rmi.log.9B={0}: method:[{1}], class:[{2}].
625: serverCallsLog.log(RMILog.VERBOSE, Messages.getString(
626: "rmi.log.9B", //$NON-NLS-1$
627: new Object[] { client, m, implClassName }));
628: }
629: }
630:
631: // Logs Exception thrown by remote method call.
632: private void logServerException(String m, Throwable t) {
633: if (printST || serverCallsLog.isLoggable(RMILog.BRIEF)) {
634: String client = ServerConnectionManager.getClientHost();
635:
636: if (client != null) {
637: // rmi.log.9C=from {0}
638: client = Messages.getString("rmi.log.9C", client); //$NON-NLS-1$
639: } else {
640: // rmi.log.9D=locally
641: client = Messages.getString("rmi.log.9D"); //$NON-NLS-1$
642: }
643: // rmi.log.9E=Exception thrown while calling [{0}] method of {1} class requested {2}:
644: String logMsg = Messages.getString("rmi.log.9E", //$NON-NLS-1$
645: new Object[] { m, implClassName, client });
646:
647: if (printST) {
648: synchronized (System.err) {
649: System.err.println(logMsg);
650: t.printStackTrace(System.err);
651: }
652: }
653:
654: if (serverCallsLog.isLoggable(RMILog.BRIEF)) {
655: // rmi.log.9E=Exception thrown while calling [{0}] method of {1} class requested {2}:
656: serverCallsLog.log(RMILog.BRIEF, Messages.getString(
657: "rmi.log.9E", //$NON-NLS-1$
658: new Object[] { m, implClassName, client }), t);
659: }
660: }
661: }
662: }
|