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