1/*
2 * Copyright (c) 1996, 2014, 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.transport;
27
28import java.io.IOException;
29import java.io.ObjectOutput;
30import java.rmi.MarshalException;
31import java.rmi.NoSuchObjectException;
32import java.rmi.Remote;
33import java.rmi.RemoteException;
34import java.rmi.server.LogStream;
35import java.rmi.server.ObjID;
36import java.rmi.server.RemoteCall;
37import java.rmi.server.RemoteServer;
38import java.rmi.server.ServerNotActiveException;
39import java.security.AccessControlContext;
40import java.security.AccessController;
41import java.security.Permissions;
42import java.security.PrivilegedAction;
43import java.security.ProtectionDomain;
44import sun.rmi.runtime.Log;
45import sun.rmi.server.Dispatcher;
46import sun.rmi.server.UnicastServerRef;
47
48/**
49 * Transport abstraction for enabling communication between different
50 * VMs.
51 *
52 * @author Ann Wollrath
53 */
54@SuppressWarnings("deprecation")
55public abstract class Transport {
56
57    /** "transport" package log level */
58    static final int logLevel = LogStream.parseLevel(getLogLevel());
59
60    private static String getLogLevel() {
61        return java.security.AccessController.doPrivileged(
62            (PrivilegedAction<String>) () -> System.getProperty("sun.rmi.transport.logLevel"));
63    }
64
65    /* transport package log */
66    static final Log transportLog =
67        Log.getLog("sun.rmi.transport.misc", "transport", Transport.logLevel);
68
69    /** References the current transport when a call is being serviced */
70    private static final ThreadLocal<Transport> currentTransport = new ThreadLocal<>();
71
72    /** ObjID for DGCImpl */
73    private static final ObjID dgcID = new ObjID(ObjID.DGC_ID);
74
75    /** AccessControlContext for setting context ClassLoader */
76    private static final AccessControlContext SETCCL_ACC;
77    static {
78        Permissions perms = new Permissions();
79        perms.add(new RuntimePermission("setContextClassLoader"));
80        ProtectionDomain[] pd = { new ProtectionDomain(null, perms) };
81        SETCCL_ACC = new AccessControlContext(pd);
82    }
83
84    /**
85     * Returns a <I>Channel</I> that generates connections to the
86     * endpoint <I>ep</I>. A Channel is an object that creates and
87     * manages connections of a particular type to some particular
88     * address space.
89     * @param ep the endpoint to which connections will be generated.
90     * @return the channel or null if the transport cannot
91     * generate connections to this endpoint
92     */
93    public abstract Channel getChannel(Endpoint ep);
94
95    /**
96     * Removes the <I>Channel</I> that generates connections to the
97     * endpoint <I>ep</I>.
98     */
99    public abstract void free(Endpoint ep);
100
101    /**
102     * Export the object so that it can accept incoming calls.
103     */
104    public void exportObject(Target target) throws RemoteException {
105        target.setExportedTransport(this);
106        ObjectTable.putTarget(target);
107    }
108
109    /**
110     * Invoked when an object that was exported on this transport has
111     * become unexported, either by being garbage collected or by
112     * being explicitly unexported.
113     **/
114    protected void targetUnexported() { }
115
116    /**
117     * Returns the current transport if a call is being serviced, otherwise
118     * returns null.
119     **/
120    static Transport currentTransport() {
121        return currentTransport.get();
122    }
123
124    /**
125     * Verify that the current access control context has permission to accept
126     * the connection being dispatched by the current thread.  The current
127     * access control context is passed as a parameter to avoid the overhead of
128     * an additional call to AccessController.getContext.
129     */
130    protected abstract void checkAcceptPermission(AccessControlContext acc);
131
132    /**
133     * Sets the context class loader for the current thread.
134     */
135    private static void setContextClassLoader(ClassLoader ccl) {
136        AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
137                Thread.currentThread().setContextClassLoader(ccl);
138                return null;
139            }, SETCCL_ACC);
140    }
141
142    /**
143     * Service an incoming remote call. When a message arrives on the
144     * connection indicating the beginning of a remote call, the
145     * threads are required to call the <I>serviceCall</I> method of
146     * their transport.  The default implementation of this method
147     * locates and calls the dispatcher object.  Ordinarily a
148     * transport implementation will not need to override this method.
149     * At the entry to <I>tr.serviceCall(conn)</I>, the connection's
150     * input stream is positioned at the start of the incoming
151     * message.  The <I>serviceCall</I> method processes the incoming
152     * remote invocation and sends the result on the connection's
153     * output stream.  If it returns "true", then the remote
154     * invocation was processed without error and the transport can
155     * cache the connection.  If it returns "false", a protocol error
156     * occurred during the call, and the transport should destroy the
157     * connection.
158     */
159    public boolean serviceCall(final RemoteCall call) {
160        try {
161            /* read object id */
162            final Remote impl;
163            ObjID id;
164
165            try {
166                id = ObjID.read(call.getInputStream());
167            } catch (java.io.IOException e) {
168                throw new MarshalException("unable to read objID", e);
169            }
170
171            /* get the remote object */
172            Transport transport = id.equals(dgcID) ? null : this;
173            Target target =
174                ObjectTable.getTarget(new ObjectEndpoint(id, transport));
175
176            if (target == null || (impl = target.getImpl()) == null) {
177                throw new NoSuchObjectException("no such object in table");
178            }
179
180            final Dispatcher disp = target.getDispatcher();
181            target.incrementCallCount();
182            try {
183                /* call the dispatcher */
184                transportLog.log(Log.VERBOSE, "call dispatcher");
185
186                final AccessControlContext acc =
187                    target.getAccessControlContext();
188                ClassLoader ccl = target.getContextClassLoader();
189
190                ClassLoader savedCcl = Thread.currentThread().getContextClassLoader();
191
192                try {
193                    setContextClassLoader(ccl);
194                    currentTransport.set(this);
195                    try {
196                        java.security.AccessController.doPrivileged(
197                            new java.security.PrivilegedExceptionAction<Void>() {
198                            public Void run() throws IOException {
199                                checkAcceptPermission(acc);
200                                disp.dispatch(impl, call);
201                                return null;
202                            }
203                        }, acc);
204                    } catch (java.security.PrivilegedActionException pae) {
205                        throw (IOException) pae.getException();
206                    }
207                } finally {
208                    setContextClassLoader(savedCcl);
209                    currentTransport.set(null);
210                }
211
212            } catch (IOException ex) {
213                transportLog.log(Log.BRIEF,
214                                 "exception thrown by dispatcher: ", ex);
215                return false;
216            } finally {
217                target.decrementCallCount();
218            }
219
220        } catch (RemoteException e) {
221
222            // if calls are being logged, write out exception
223            if (UnicastServerRef.callLog.isLoggable(Log.BRIEF)) {
224                // include client host name if possible
225                String clientHost = "";
226                try {
227                    clientHost = "[" +
228                        RemoteServer.getClientHost() + "] ";
229                } catch (ServerNotActiveException ex) {
230                }
231                String message = clientHost + "exception: ";
232                UnicastServerRef.callLog.log(Log.BRIEF, message, e);
233            }
234
235            /* We will get a RemoteException if either a) the objID is
236             * not readable, b) the target is not in the object table, or
237             * c) the object is in the midst of being unexported (note:
238             * NoSuchObjectException is thrown by the incrementCallCount
239             * method if the object is being unexported).  Here it is
240             * relatively safe to marshal an exception to the client
241             * since the client will not have seen a return value yet.
242             */
243            try {
244                ObjectOutput out = call.getResultStream(false);
245                UnicastServerRef.clearStackTraces(e);
246                out.writeObject(e);
247                call.releaseOutputStream();
248
249            } catch (IOException ie) {
250                transportLog.log(Log.BRIEF,
251                    "exception thrown marshalling exception: ", ie);
252                return false;
253            }
254        }
255
256        return true;
257    }
258}
259