1 /* 2 * Copyright (c) 2002, 2016, 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 javax.management.remote.rmi; 27 28 import java.io.IOException; 29 import java.rmi.NoSuchObjectException; 30 import java.rmi.Remote; 31 import java.rmi.RemoteException; 32 import java.rmi.server.RMIClientSocketFactory; 33 import java.rmi.server.RMIServerSocketFactory; 34 import java.rmi.server.UnicastRemoteObject; 35 import java.rmi.server.RemoteObject; 36 import java.util.Map; 37 import java.util.Collections; 38 import javax.security.auth.Subject; 39 40 import com.sun.jmx.remote.internal.rmi.RMIExporter; 41 import com.sun.jmx.remote.util.EnvHelp; 42 import java.io.ObjectStreamClass; 43 import java.lang.reflect.Method; 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.List; 47 import sun.reflect.misc.ReflectUtil; 48 import sun.rmi.server.DeserializationChecker; 49 import sun.rmi.server.UnicastServerRef; 50 import sun.rmi.server.UnicastServerRef2; 51 52 /** 53 * <p>An {@link RMIServer} object that is exported through JRMP and that 54 * creates client connections as RMI objects exported through JRMP. 55 * User code does not usually reference this class directly.</p> 56 * 57 * @see RMIServerImpl 58 * 59 * @since 1.5 60 */ 61 public class RMIJRMPServerImpl extends RMIServerImpl { 62 63 private final ExportedWrapper exportedWrapper; 64 65 /** 66 * <p>Creates a new {@link RMIServer} object that will be exported 67 * on the given port using the given socket factories.</p> 68 * 69 * @param port the port on which this object and the {@link 70 * RMIConnectionImpl} objects it creates will be exported. Can be 71 * zero, to indicate any available port. 72 * 73 * @param csf the client socket factory for the created RMI 74 * objects. Can be null. 75 * 76 * @param ssf the server socket factory for the created RMI 77 * objects. Can be null. 78 * 79 * @param env the environment map. Can be null. 80 * 81 * @exception IOException if the {@link RMIServer} object 82 * cannot be created. 83 * 84 * @exception IllegalArgumentException if <code>port</code> is 85 * negative. 86 */ 87 public RMIJRMPServerImpl(int port, 88 RMIClientSocketFactory csf, 89 RMIServerSocketFactory ssf, 90 Map<String,?> env) 91 throws IOException { 92 93 super(env); 94 95 if (port < 0) 96 throw new IllegalArgumentException("Negative port: " + port); 97 98 this.port = port; 99 this.csf = csf; 100 this.ssf = ssf; 101 this.env = (env == null) ? Collections.<String, Object>emptyMap() : env; 102 103 String[] credentialsTypes 104 = (String[]) this.env.get(RMIConnectorServer.CREDENTIAL_TYPES); 105 List<String> types = null; 106 if (credentialsTypes != null) { 107 types = new ArrayList<>(); 108 for (String type : credentialsTypes) { 109 if (type == null) { 110 throw new IllegalArgumentException("A credential type is null."); 111 } 112 ReflectUtil.checkPackageAccess(type); 113 types.add(type); 114 } 115 } 116 exportedWrapper = types != null ? 117 new ExportedWrapper(this, types) : 118 null; 119 } 120 121 protected void export() throws IOException { 122 if (exportedWrapper != null) { 123 export(exportedWrapper); 124 } else { 125 export(this); 126 } 127 } 128 129 private void export(Remote obj) throws RemoteException { 130 final RMIExporter exporter = 131 (RMIExporter) env.get(RMIExporter.EXPORTER_ATTRIBUTE); 132 final boolean daemon = EnvHelp.isServerDaemon(env); 133 134 if (daemon && exporter != null) { 135 throw new IllegalArgumentException("If "+EnvHelp.JMX_SERVER_DAEMON+ 136 " is specified as true, "+RMIExporter.EXPORTER_ATTRIBUTE+ 137 " cannot be used to specify an exporter!"); 138 } 139 140 if (daemon) { 141 if (csf == null && ssf == null) { 142 new UnicastServerRef(port).exportObject(obj, null, true); 143 } else { 144 new UnicastServerRef2(port, csf, ssf).exportObject(obj, null, true); 145 } 146 } else if (exporter != null) { 147 exporter.exportObject(obj, port, csf, ssf); 148 } else { 149 UnicastRemoteObject.exportObject(obj, port, csf, ssf); 150 } 151 } 152 153 private void unexport(Remote obj, boolean force) 154 throws NoSuchObjectException { 155 RMIExporter exporter = 156 (RMIExporter) env.get(RMIExporter.EXPORTER_ATTRIBUTE); 157 if (exporter == null) 158 UnicastRemoteObject.unexportObject(obj, force); 159 else 160 exporter.unexportObject(obj, force); 161 } 162 163 protected String getProtocol() { 164 return "rmi"; 165 } 166 167 /** 168 * <p>Returns a serializable stub for this {@link RMIServer} object.</p> 169 * 170 * @return a serializable stub. 171 * 172 * @exception IOException if the stub cannot be obtained - e.g the 173 * RMIJRMPServerImpl has not been exported yet. 174 */ 175 public Remote toStub() throws IOException { 176 if (exportedWrapper != null) { 177 return RemoteObject.toStub(exportedWrapper); 178 } else { 179 return RemoteObject.toStub(this); 180 } 181 } 182 183 /** 184 * <p>Creates a new client connection as an RMI object exported 185 * through JRMP. The port and socket factories for the new 186 * {@link RMIConnection} object are the ones supplied 187 * to the <code>RMIJRMPServerImpl</code> constructor.</p> 188 * 189 * @param connectionId the ID of the new connection. Every 190 * connection opened by this connector server will have a 191 * different id. The behavior is unspecified if this parameter is 192 * null. 193 * 194 * @param subject the authenticated subject. Can be null. 195 * 196 * @return the newly-created <code>RMIConnection</code>. 197 * 198 * @exception IOException if the new {@link RMIConnection} 199 * object cannot be created or exported. 200 */ 201 protected RMIConnection makeClient(String connectionId, Subject subject) 202 throws IOException { 203 204 if (connectionId == null) 205 throw new NullPointerException("Null connectionId"); 206 207 RMIConnection client = 208 new RMIConnectionImpl(this, connectionId, getDefaultClassLoader(), 209 subject, env); 210 export(client); 211 return client; 212 } 213 214 protected void closeClient(RMIConnection client) throws IOException { 215 unexport(client, true); 216 } 217 218 /** 219 * <p>Called by {@link #close()} to close the connector server by 220 * unexporting this object. After returning from this method, the 221 * connector server must not accept any new connections.</p> 222 * 223 * @exception IOException if the attempt to close the connector 224 * server failed. 225 */ 226 protected void closeServer() throws IOException { 227 if (exportedWrapper != null) { 228 unexport(exportedWrapper, true); 229 } else { 230 unexport(this, true); 231 } 232 } 233 234 private final int port; 235 private final RMIClientSocketFactory csf; 236 private final RMIServerSocketFactory ssf; 237 private final Map<String, ?> env; 238 239 private static class ExportedWrapper implements RMIServer, DeserializationChecker { 240 private final RMIServer impl; 241 private final List<String> allowedTypes; 242 243 private ExportedWrapper(RMIServer impl, List<String> credentialsTypes) { 244 this.impl = impl; 245 allowedTypes = credentialsTypes; 246 } 247 248 @Override 249 public String getVersion() throws RemoteException { 250 return impl.getVersion(); 251 } 252 253 @Override 254 public RMIConnection newClient(Object credentials) throws IOException { 255 return impl.newClient(credentials); 256 } 257 258 @Override 259 public void check(Method method, ObjectStreamClass descriptor, 260 int paramIndex, int callID) { 261 String type = descriptor.getName(); 262 if (!allowedTypes.contains(type)) { 263 throw new ClassCastException("Unsupported type: " + type); 264 } 265 } 266 267 @Override 268 public void checkProxyClass(Method method, String[] ifaces, 269 int paramIndex, int callID) { 270 if (ifaces != null && ifaces.length > 0) { 271 for (String iface : ifaces) { 272 if (!allowedTypes.contains(iface)) { 273 throw new ClassCastException("Unsupported type: " + iface); 274 } 275 } 276 } 277 } 278 } 279 }