1/*
2 * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.rmi.server;
27
28import java.io.IOException;
29import java.io.ObjectInput;
30import java.io.ObjectOutput;
31import java.lang.reflect.Method;
32import java.rmi.MarshalException;
33import java.rmi.Remote;
34import java.rmi.RemoteException;
35import java.rmi.UnmarshalException;
36import java.rmi.server.Operation;
37import java.rmi.server.RemoteCall;
38import java.rmi.server.RemoteObject;
39import java.rmi.server.RemoteRef;
40import java.security.AccessController;
41import java.security.PrivilegedAction;
42import sun.rmi.runtime.Log;
43import sun.rmi.transport.Connection;
44import sun.rmi.transport.LiveRef;
45import sun.rmi.transport.StreamRemoteCall;
46
47/**
48 * NOTE: There is a JDK-internal dependency on the existence of this
49 * class's getLiveRef method (as it is inherited by UnicastRef2) in
50 * the implementation of javax.management.remote.rmi.RMIConnector.
51 */
52@SuppressWarnings("deprecation")
53public class UnicastRef implements RemoteRef {
54
55    /**
56     * Client-side transport log.
57     */
58    public static final Log clientRefLog =
59        Log.getLog("sun.rmi.client.ref", "transport",  Util.logLevel);
60
61    /**
62     * Client-side call log.
63     */
64    public static final Log clientCallLog =
65        Log.getLog("sun.rmi.client.call", "RMI",
66                   AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
67                       Boolean.getBoolean("sun.rmi.client.logCalls")));
68    private static final long serialVersionUID = 8258372400816541186L;
69
70    protected LiveRef ref;
71
72    /**
73     * Create a new (empty) Unicast remote reference.
74     */
75    public UnicastRef() {
76    }
77
78    /**
79     * Create a new Unicast RemoteRef.
80     */
81    public UnicastRef(LiveRef liveRef) {
82        ref = liveRef;
83    }
84
85    /**
86     * Returns the current value of this UnicastRef's underlying
87     * LiveRef.
88     *
89     * NOTE: There is a JDK-internal dependency on the existence of
90     * this method (as it is inherited by UnicastRef) in the
91     * implementation of javax.management.remote.rmi.RMIConnector.
92     **/
93    public LiveRef getLiveRef() {
94        return ref;
95    }
96
97    /**
98     * Invoke a method. This form of delegating method invocation
99     * to the reference allows the reference to take care of
100     * setting up the connection to the remote host, marshalling
101     * some representation for the method and parameters, then
102     * communicating the method invocation to the remote host.
103     * This method either returns the result of a method invocation
104     * on the remote object which resides on the remote host or
105     * throws a RemoteException if the call failed or an
106     * application-level exception if the remote invocation throws
107     * an exception.
108     *
109     * @param obj the proxy for the remote object
110     * @param method the method to be invoked
111     * @param params the parameter list
112     * @param opnum  a hash that may be used to represent the method
113     * @since 1.2
114     */
115    public Object invoke(Remote obj,
116                         Method method,
117                         Object[] params,
118                         long opnum)
119        throws Exception
120    {
121        if (clientRefLog.isLoggable(Log.VERBOSE)) {
122            clientRefLog.log(Log.VERBOSE, "method: " + method);
123        }
124
125        if (clientCallLog.isLoggable(Log.VERBOSE)) {
126            logClientCall(obj, method);
127        }
128
129        Connection conn = ref.getChannel().newConnection();
130        RemoteCall call = null;
131        boolean reuse = true;
132
133        /* If the call connection is "reused" early, remember not to
134         * reuse again.
135         */
136        boolean alreadyFreed = false;
137
138        try {
139            if (clientRefLog.isLoggable(Log.VERBOSE)) {
140                clientRefLog.log(Log.VERBOSE, "opnum = " + opnum);
141            }
142
143            // create call context
144            call = new StreamRemoteCall(conn, ref.getObjID(), -1, opnum);
145
146            // marshal parameters
147            try {
148                ObjectOutput out = call.getOutputStream();
149                marshalCustomCallData(out);
150                Class<?>[] types = method.getParameterTypes();
151                for (int i = 0; i < types.length; i++) {
152                    marshalValue(types[i], params[i], out);
153                }
154            } catch (IOException e) {
155                clientRefLog.log(Log.BRIEF,
156                    "IOException marshalling arguments: ", e);
157                throw new MarshalException("error marshalling arguments", e);
158            }
159
160            // unmarshal return
161            call.executeCall();
162
163            try {
164                Class<?> rtype = method.getReturnType();
165                if (rtype == void.class)
166                    return null;
167                ObjectInput in = call.getInputStream();
168
169                /* StreamRemoteCall.done() does not actually make use
170                 * of conn, therefore it is safe to reuse this
171                 * connection before the dirty call is sent for
172                 * registered refs.
173                 */
174                Object returnValue = unmarshalValue(rtype, in);
175
176                /* we are freeing the connection now, do not free
177                 * again or reuse.
178                 */
179                alreadyFreed = true;
180
181                /* if we got to this point, reuse must have been true. */
182                clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");
183
184                /* Free the call's connection early. */
185                ref.getChannel().free(conn, true);
186
187                return returnValue;
188
189            } catch (IOException | ClassNotFoundException e) {
190                // disable saving any refs in the inputStream for GC
191                ((StreamRemoteCall)call).discardPendingRefs();
192                clientRefLog.log(Log.BRIEF,
193                                 e.getClass().getName() + " unmarshalling return: ", e);
194                throw new UnmarshalException("error unmarshalling return", e);
195            } finally {
196                try {
197                    call.done();
198                } catch (IOException e) {
199                    /* WARNING: If the conn has been reused early,
200                     * then it is too late to recover from thrown
201                     * IOExceptions caught here. This code is relying
202                     * on StreamRemoteCall.done() not actually
203                     * throwing IOExceptions.
204                     */
205                    reuse = false;
206                }
207            }
208
209        } catch (RuntimeException e) {
210            /*
211             * Need to distinguish between client (generated by the
212             * invoke method itself) and server RuntimeExceptions.
213             * Client side RuntimeExceptions are likely to have
214             * corrupted the call connection and those from the server
215             * are not likely to have done so.  If the exception came
216             * from the server the call connection should be reused.
217             */
218            if ((call == null) ||
219                (((StreamRemoteCall) call).getServerException() != e))
220            {
221                reuse = false;
222            }
223            throw e;
224
225        } catch (RemoteException e) {
226            /*
227             * Some failure during call; assume connection cannot
228             * be reused.  Must assume failure even if ServerException
229             * or ServerError occurs since these failures can happen
230             * during parameter deserialization which would leave
231             * the connection in a corrupted state.
232             */
233            reuse = false;
234            throw e;
235
236        } catch (Error e) {
237            /* If errors occurred, the connection is most likely not
238             *  reusable.
239             */
240            reuse = false;
241            throw e;
242
243        } finally {
244
245            /* alreadyFreed ensures that we do not log a reuse that
246             * may have already happened.
247             */
248            if (!alreadyFreed) {
249                if (clientRefLog.isLoggable(Log.BRIEF)) {
250                    clientRefLog.log(Log.BRIEF, "free connection (reuse = " +
251                                           reuse + ")");
252                }
253                ref.getChannel().free(conn, reuse);
254            }
255        }
256    }
257
258    protected void marshalCustomCallData(ObjectOutput out) throws IOException
259    {}
260
261    /**
262     * Marshal value to an ObjectOutput sink using RMI's serialization
263     * format for parameters or return values.
264     */
265    protected static void marshalValue(Class<?> type, Object value,
266                                       ObjectOutput out)
267        throws IOException
268    {
269        if (type.isPrimitive()) {
270            if (type == int.class) {
271                out.writeInt(((Integer) value).intValue());
272            } else if (type == boolean.class) {
273                out.writeBoolean(((Boolean) value).booleanValue());
274            } else if (type == byte.class) {
275                out.writeByte(((Byte) value).byteValue());
276            } else if (type == char.class) {
277                out.writeChar(((Character) value).charValue());
278            } else if (type == short.class) {
279                out.writeShort(((Short) value).shortValue());
280            } else if (type == long.class) {
281                out.writeLong(((Long) value).longValue());
282            } else if (type == float.class) {
283                out.writeFloat(((Float) value).floatValue());
284            } else if (type == double.class) {
285                out.writeDouble(((Double) value).doubleValue());
286            } else {
287                throw new Error("Unrecognized primitive type: " + type);
288            }
289        } else {
290            out.writeObject(value);
291        }
292    }
293
294    /**
295     * Unmarshal value from an ObjectInput source using RMI's serialization
296     * format for parameters or return values.
297     */
298    protected static Object unmarshalValue(Class<?> type, ObjectInput in)
299        throws IOException, ClassNotFoundException
300    {
301        if (type.isPrimitive()) {
302            if (type == int.class) {
303                return Integer.valueOf(in.readInt());
304            } else if (type == boolean.class) {
305                return Boolean.valueOf(in.readBoolean());
306            } else if (type == byte.class) {
307                return Byte.valueOf(in.readByte());
308            } else if (type == char.class) {
309                return Character.valueOf(in.readChar());
310            } else if (type == short.class) {
311                return Short.valueOf(in.readShort());
312            } else if (type == long.class) {
313                return Long.valueOf(in.readLong());
314            } else if (type == float.class) {
315                return Float.valueOf(in.readFloat());
316            } else if (type == double.class) {
317                return Double.valueOf(in.readDouble());
318            } else {
319                throw new Error("Unrecognized primitive type: " + type);
320            }
321        } else {
322            return in.readObject();
323        }
324    }
325
326    /**
327     * Create an appropriate call object for a new call on this object.
328     * Passing operation array and index, allows the stubs generator to
329     * assign the operation indexes and interpret them. The RemoteRef
330     * may need the operation to encode in for the call.
331     */
332    public RemoteCall newCall(RemoteObject obj, Operation[] ops, int opnum,
333                              long hash)
334        throws RemoteException
335    {
336        clientRefLog.log(Log.BRIEF, "get connection");
337
338        Connection conn = ref.getChannel().newConnection();
339        try {
340            clientRefLog.log(Log.VERBOSE, "create call context");
341
342            /* log information about the outgoing call */
343            if (clientCallLog.isLoggable(Log.VERBOSE)) {
344                logClientCall(obj, ops[opnum]);
345            }
346
347            RemoteCall call =
348                new StreamRemoteCall(conn, ref.getObjID(), opnum, hash);
349            try {
350                marshalCustomCallData(call.getOutputStream());
351            } catch (IOException e) {
352                throw new MarshalException("error marshaling " +
353                                           "custom call data");
354            }
355            return call;
356        } catch (RemoteException e) {
357            ref.getChannel().free(conn, false);
358            throw e;
359        }
360    }
361
362    /**
363     * Invoke makes the remote call present in the RemoteCall object.
364     *
365     * Invoke will raise any "user" exceptions which
366     * should pass through and not be caught by the stub.  If any
367     * exception is raised during the remote invocation, invoke should
368     * take care of cleaning up the connection before raising the
369     * "user" or remote exception.
370     */
371    public void invoke(RemoteCall call) throws Exception {
372        try {
373            clientRefLog.log(Log.VERBOSE, "execute call");
374
375            call.executeCall();
376
377        } catch (RemoteException e) {
378            /*
379             * Call did not complete; connection can't be reused.
380             */
381            clientRefLog.log(Log.BRIEF, "exception: ", e);
382            free(call, false);
383            throw e;
384
385        } catch (Error e) {
386            /* If errors occurred, the connection is most likely not
387             *  reusable.
388             */
389            clientRefLog.log(Log.BRIEF, "error: ", e);
390            free(call, false);
391            throw e;
392
393        } catch (RuntimeException e) {
394            /*
395             * REMIND: Since runtime exceptions are no longer wrapped,
396             * we can't assue that the connection was left in
397             * a reusable state. Is this okay?
398             */
399            clientRefLog.log(Log.BRIEF, "exception: ", e);
400            free(call, false);
401            throw e;
402
403        } catch (Exception e) {
404            /*
405             * Assume that these other exceptions are user exceptions
406             * and leave the connection in a reusable state.
407             */
408            clientRefLog.log(Log.BRIEF, "exception: ", e);
409            free(call, true);
410            /* reraise user (and unknown) exceptions. */
411            throw e;
412        }
413
414        /*
415         * Don't free the connection if an exception did not
416         * occur because the stub needs to unmarshal the
417         * return value. The connection will be freed
418         * by a call to the "done" method.
419         */
420    }
421
422    /**
423     * Private method to free a connection.
424     */
425    private void free(RemoteCall call, boolean reuse) throws RemoteException {
426        Connection conn = ((StreamRemoteCall)call).getConnection();
427        ref.getChannel().free(conn, reuse);
428    }
429
430    /**
431     * Done should only be called if the invoke returns successfully
432     * (non-exceptionally) to the stub. It allows the remote reference to
433     * clean up (or reuse) the connection.
434     */
435    public void done(RemoteCall call) throws RemoteException {
436
437        /* Done only uses the connection inside the call to obtain the
438         * channel the connection uses.  Once all information is read
439         * from the connection, the connection may be freed.
440         */
441        clientRefLog.log(Log.BRIEF, "free connection (reuse = true)");
442
443        /* Free the call connection early. */
444        free(call, true);
445
446        try {
447            call.done();
448        } catch (IOException e) {
449            /* WARNING: If the conn has been reused early, then it is
450             * too late to recover from thrown IOExceptions caught
451             * here. This code is relying on StreamRemoteCall.done()
452             * not actually throwing IOExceptions.
453             */
454        }
455    }
456
457    /**
458     * Log the details of an outgoing call.  The method parameter is either of
459     * type java.lang.reflect.Method or java.rmi.server.Operation.
460     */
461    void logClientCall(Object obj, Object method) {
462        clientCallLog.log(Log.VERBOSE, "outbound call: " +
463            ref + " : " + obj.getClass().getName() +
464            ref.getObjID().toString() + ": " + method);
465    }
466
467    /**
468     * Returns the class of the ref type to be serialized
469     */
470    public String getRefClass(ObjectOutput out) {
471        return "UnicastRef";
472    }
473
474    /**
475     * Write out external representation for remote ref.
476     */
477    public void writeExternal(ObjectOutput out) throws IOException {
478        ref.write(out, false);
479    }
480
481    /**
482     * Read in external representation for remote ref.
483     * @exception ClassNotFoundException If the class for an object
484     * being restored cannot be found.
485     */
486    public void readExternal(ObjectInput in)
487        throws IOException, ClassNotFoundException
488    {
489        ref = LiveRef.read(in, false);
490    }
491
492    //----------------------------------------------------------------------;
493    /**
494     * Method from object, forward from RemoteObject
495     */
496    public String remoteToString() {
497        return Util.getUnqualifiedName(getClass()) + " [liveRef: " + ref + "]";
498    }
499
500    /**
501     * default implementation of hashCode for remote objects
502     */
503    public int remoteHashCode() {
504        return ref.hashCode();
505    }
506
507    /** default implementation of equals for remote objects
508     */
509    public boolean remoteEquals(RemoteRef sub) {
510        if (sub instanceof UnicastRef)
511            return ref.remoteEquals(((UnicastRef)sub).ref);
512        return false;
513    }
514}
515