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.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 } |
|