blob: 0463db83f89002edeb7369ab1d8e2f9dda00ad5c [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.management.jmxremote;
27
28import java.io.BufferedInputStream;
29import java.io.File;
30import java.io.FileInputStream;
31import java.io.InputStream;
32import java.io.IOException;
33
34import java.net.InetAddress;
35import java.net.MalformedURLException;
36import java.net.UnknownHostException;
37
38import java.rmi.NoSuchObjectException;
39import java.rmi.Remote;
40import java.rmi.RemoteException;
41import java.rmi.registry.Registry;
42import java.rmi.server.RMIClientSocketFactory;
43import java.rmi.server.RMIServerSocketFactory;
44import java.rmi.server.UnicastRemoteObject;
45
46import java.security.KeyStore;
47import java.security.Principal;
48
49import java.util.HashMap;
50import java.util.HashSet;
51import java.util.Iterator;
52import java.util.Map;
53import java.util.Properties;
54import java.util.Set;
55import java.util.StringTokenizer;
56
57import java.lang.management.ManagementFactory;
58
59import javax.net.ssl.*;
60
61import javax.management.MBeanServer;
62import javax.management.remote.JMXAuthenticator;
63import javax.management.remote.JMXConnectorServer;
64import javax.management.remote.JMXConnectorServerFactory;
65import javax.management.remote.JMXServiceURL;
66import javax.management.remote.rmi.RMIConnectorServer;
67
68import javax.rmi.ssl.SslRMIClientSocketFactory;
69import javax.rmi.ssl.SslRMIServerSocketFactory;
70
71import javax.security.auth.Subject;
72
73import sun.rmi.server.UnicastServerRef;
74import sun.rmi.server.UnicastServerRef2;
75
76import sun.management.Agent;
77import sun.management.AgentConfigurationError;
78import static sun.management.AgentConfigurationError.*;
79import sun.management.FileSystem;
80import sun.management.snmp.util.MibLogger;
81
82import com.sun.jmx.remote.internal.RMIExporter;
83import com.sun.jmx.remote.security.JMXPluggableAuthenticator;
84
85/**
86 * This class initializes and starts the RMIConnectorServer for JSR 163
87 * JMX Monitoring.
88 **/
89public final class ConnectorBootstrap {
90
91 /**
92 * Default values for JMX configuration properties.
93 **/
94 public static interface DefaultValues {
95 public static final String PORT="0";
96 public static final String CONFIG_FILE_NAME="management.properties";
97 public static final String USE_SSL="true";
98 public static final String USE_REGISTRY_SSL="false";
99 public static final String USE_AUTHENTICATION="true";
100 public static final String PASSWORD_FILE_NAME="jmxremote.password";
101 public static final String ACCESS_FILE_NAME="jmxremote.access";
102 public static final String SSL_NEED_CLIENT_AUTH="false";
103 }
104
105 /**
106 * Names of JMX configuration properties.
107 **/
108 public static interface PropertyNames {
109 public static final String PORT =
110 "com.sun.management.jmxremote.port";
111 public static final String CONFIG_FILE_NAME =
112 "com.sun.management.config.file";
113 public static final String USE_SSL =
114 "com.sun.management.jmxremote.ssl";
115 public static final String USE_REGISTRY_SSL =
116 "com.sun.management.jmxremote.registry.ssl";
117 public static final String USE_AUTHENTICATION =
118 "com.sun.management.jmxremote.authenticate";
119 public static final String PASSWORD_FILE_NAME =
120 "com.sun.management.jmxremote.password.file";
121 public static final String ACCESS_FILE_NAME =
122 "com.sun.management.jmxremote.access.file";
123 public static final String LOGIN_CONFIG_NAME =
124 "com.sun.management.jmxremote.login.config";
125 public static final String SSL_ENABLED_CIPHER_SUITES =
126 "com.sun.management.jmxremote.ssl.enabled.cipher.suites";
127 public static final String SSL_ENABLED_PROTOCOLS =
128 "com.sun.management.jmxremote.ssl.enabled.protocols";
129 public static final String SSL_NEED_CLIENT_AUTH =
130 "com.sun.management.jmxremote.ssl.need.client.auth";
131 public static final String SSL_CONFIG_FILE_NAME =
132 "com.sun.management.jmxremote.ssl.config.file";
133 }
134
135 /**
136 * <p>Prevents our RMI server objects from keeping the JVM alive.</p>
137 *
138 * <p>We use a private interface in Sun's JMX Remote API implementation
139 * that allows us to specify how to export RMI objects. We do so using
140 * UnicastServerRef, a class in Sun's RMI implementation. This is all
141 * non-portable, of course, so this is only valid because we are inside
142 * Sun's JRE.</p>
143 *
144 * <p>Objects are exported using {@link
145 * UnicastServerRef#exportObject(Remote, Object, boolean)}. The
146 * boolean parameter is called <code>permanent</code> and means
147 * both that the object is not eligible for Distributed Garbage
148 * Collection, and that its continued existence will not prevent
149 * the JVM from exiting. It is the latter semantics we want (we
150 * already have the former because of the way the JMX Remote API
151 * works). Hence the somewhat misleading name of this class.</p>
152 */
153 private static class PermanentExporter implements RMIExporter {
154 public Remote exportObject(Remote obj,
155 int port,
156 RMIClientSocketFactory csf,
157 RMIServerSocketFactory ssf)
158 throws RemoteException {
159
160 synchronized (this) {
161 if (firstExported == null)
162 firstExported = obj;
163 }
164
165 final UnicastServerRef ref;
166 if (csf == null && ssf == null)
167 ref = new UnicastServerRef(port);
168 else
169 ref = new UnicastServerRef2(port, csf, ssf);
170 return ref.exportObject(obj, null, true);
171 }
172
173 // Nothing special to be done for this case
174 public boolean unexportObject(Remote obj, boolean force)
175 throws NoSuchObjectException {
176 return UnicastRemoteObject.unexportObject(obj, force);
177 }
178
179 Remote firstExported;
180 }
181
182 /**
183 * This JMXAuthenticator wraps the JMXPluggableAuthenticator and verifies
184 * that at least one of the principal names contained in the authenticated
185 * Subject is present in the access file.
186 */
187 private static class AccessFileCheckerAuthenticator
188 implements JMXAuthenticator {
189
190 public AccessFileCheckerAuthenticator(Map<String, Object> env) throws IOException {
191 environment = env;
192 accessFile = (String) env.get("jmx.remote.x.access.file");
193 properties = propertiesFromFile(accessFile);
194 }
195
196 public Subject authenticate(Object credentials) {
197 final JMXAuthenticator authenticator =
198 new JMXPluggableAuthenticator(environment);
199 final Subject subject = authenticator.authenticate(credentials);
200 checkAccessFileEntries(subject);
201 return subject;
202 }
203
204 private void checkAccessFileEntries(Subject subject) {
205 if (subject == null)
206 throw new SecurityException(
207 "Access denied! No matching entries found in " +
208 "the access file [" + accessFile + "] as the " +
209 "authenticated Subject is null");
210 final Set principals = subject.getPrincipals();
211 for (Iterator i = principals.iterator(); i.hasNext(); ) {
212 final Principal p = (Principal) i.next();
213 if (properties.containsKey(p.getName()))
214 return;
215 }
216 final Set<String> principalsStr = new HashSet<String>();
217 for (Iterator i = principals.iterator(); i.hasNext(); ) {
218 final Principal p = (Principal) i.next();
219 principalsStr.add(p.getName());
220 }
221 throw new SecurityException(
222 "Access denied! No entries found in the access file [" +
223 accessFile + "] for any of the authenticated identities " +
224 principalsStr);
225 }
226
227 private static Properties propertiesFromFile(String fname)
228 throws IOException {
229 Properties p = new Properties();
230 if (fname == null)
231 return p;
232 FileInputStream fin = new FileInputStream(fname);
233 p.load(fin);
234 fin.close();
235 return p;
236 }
237
238 private final Map<String, Object> environment;
239 private final Properties properties;
240 private final String accessFile;
241 }
242
243 /**
244 * Initializes and starts the JMX Connector Server.
245 * If the com.sun.management.jmxremote.port property is not defined,
246 * simply return. Otherwise, attempts to load the config file, and
247 * then calls {@link #initialize(java.lang.String, java.util.Properties)}.
248 *
249 **/
250 public static synchronized JMXConnectorServer initialize() {
251
252 // Load a new management properties
253 final Properties props = Agent.loadManagementProperties();
254 if (props == null) return null;
255
256 final String portStr = props.getProperty(PropertyNames.PORT);
257
258
259 // System.out.println("initializing: {port=" + portStr + ",
260 // properties="+props+"}");
261 return initialize(portStr,props);
262 }
263
264 /**
265 * Initializes and starts a JMX Connector Server for remote
266 * monitoring and management.
267 **/
268 public static synchronized
269 JMXConnectorServer initialize(String portStr, Properties props) {
270
271 // Get port number
272 final int port;
273 try {
274 port = Integer.parseInt(portStr);
275 } catch (NumberFormatException x) {
276 throw new AgentConfigurationError(INVALID_JMXREMOTE_PORT, x, portStr);
277 }
278 if (port < 0) {
279 throw new AgentConfigurationError(INVALID_JMXREMOTE_PORT, portStr);
280 }
281
282 // Do we use authentication?
283 final String useAuthenticationStr =
284 props.getProperty(PropertyNames.USE_AUTHENTICATION,
285 DefaultValues.USE_AUTHENTICATION);
286 final boolean useAuthentication =
287 Boolean.valueOf(useAuthenticationStr).booleanValue();
288
289 // Do we use SSL?
290 final String useSslStr =
291 props.getProperty(PropertyNames.USE_SSL,
292 DefaultValues.USE_SSL);
293 final boolean useSsl =
294 Boolean.valueOf(useSslStr).booleanValue();
295
296 // Do we use RMI Registry SSL?
297 final String useRegistrySslStr =
298 props.getProperty(PropertyNames.USE_REGISTRY_SSL,
299 DefaultValues.USE_REGISTRY_SSL);
300 final boolean useRegistrySsl =
301 Boolean.valueOf(useRegistrySslStr).booleanValue();
302
303 final String enabledCipherSuites =
304 props.getProperty(PropertyNames.SSL_ENABLED_CIPHER_SUITES);
305 String enabledCipherSuitesList[] = null;
306 if (enabledCipherSuites != null) {
307 StringTokenizer st = new StringTokenizer(enabledCipherSuites, ",");
308 int tokens = st.countTokens();
309 enabledCipherSuitesList = new String[tokens];
310 for (int i = 0 ; i < tokens; i++) {
311 enabledCipherSuitesList[i] = st.nextToken();
312 }
313 }
314
315 final String enabledProtocols =
316 props.getProperty(PropertyNames.SSL_ENABLED_PROTOCOLS);
317 String enabledProtocolsList[] = null;
318 if (enabledProtocols != null) {
319 StringTokenizer st = new StringTokenizer(enabledProtocols, ",");
320 int tokens = st.countTokens();
321 enabledProtocolsList = new String[tokens];
322 for (int i = 0 ; i < tokens; i++) {
323 enabledProtocolsList[i] = st.nextToken();
324 }
325 }
326
327 final String sslNeedClientAuthStr =
328 props.getProperty(PropertyNames.SSL_NEED_CLIENT_AUTH,
329 DefaultValues.SSL_NEED_CLIENT_AUTH);
330 final boolean sslNeedClientAuth =
331 Boolean.valueOf(sslNeedClientAuthStr).booleanValue();
332
333 // Read SSL config file name
334 final String sslConfigFileName =
335 props.getProperty(PropertyNames.SSL_CONFIG_FILE_NAME);
336
337 String loginConfigName = null;
338 String passwordFileName = null;
339 String accessFileName = null;
340
341 // Initialize settings when authentication is active
342 if (useAuthentication) {
343
344 // Get non-default login configuration
345 loginConfigName =
346 props.getProperty(PropertyNames.LOGIN_CONFIG_NAME);
347
348 if (loginConfigName == null) {
349 // Get password file
350 passwordFileName =
351 props.getProperty(PropertyNames.PASSWORD_FILE_NAME,
352 getDefaultFileName(DefaultValues.PASSWORD_FILE_NAME));
353 checkPasswordFile(passwordFileName);
354 }
355
356 // Get access file
357 accessFileName = props.getProperty(PropertyNames.ACCESS_FILE_NAME,
358 getDefaultFileName(DefaultValues.ACCESS_FILE_NAME));
359 checkAccessFile(accessFileName);
360 }
361
362 if (log.isDebugOn()) {
363 log.debug("initialize",
364 Agent.getText("jmxremote.ConnectorBootstrap.initialize") +
365 "\n\t" + PropertyNames.PORT + "=" + port +
366 "\n\t" + PropertyNames.USE_SSL + "=" + useSsl +
367 "\n\t" + PropertyNames.USE_REGISTRY_SSL + "=" + useRegistrySsl +
368 "\n\t" + PropertyNames.SSL_CONFIG_FILE_NAME + "=" + sslConfigFileName +
369 "\n\t" + PropertyNames.SSL_ENABLED_CIPHER_SUITES + "=" +
370 enabledCipherSuites +
371 "\n\t" + PropertyNames.SSL_ENABLED_PROTOCOLS + "=" +
372 enabledProtocols +
373 "\n\t" + PropertyNames.SSL_NEED_CLIENT_AUTH + "=" +
374 sslNeedClientAuth +
375 "\n\t" + PropertyNames.USE_AUTHENTICATION + "=" +
376 useAuthentication +
377 (useAuthentication ?
378 (loginConfigName == null ?
379 ("\n\t" + PropertyNames.PASSWORD_FILE_NAME + "=" +
380 passwordFileName) :
381 ("\n\t" + PropertyNames.LOGIN_CONFIG_NAME + "=" +
382 loginConfigName)) : "\n\t" +
383 Agent.getText("jmxremote.ConnectorBootstrap.initialize.noAuthentication")) +
384 (useAuthentication ?
385 ("\n\t" + PropertyNames.ACCESS_FILE_NAME + "=" +
386 accessFileName) : "") +
387 "");
388 }
389
390 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
391 JMXConnectorServer cs = null;
392 try {
393 cs = exportMBeanServer(mbs, port, useSsl, useRegistrySsl,
394 sslConfigFileName, enabledCipherSuitesList,
395 enabledProtocolsList, sslNeedClientAuth,
396 useAuthentication, loginConfigName,
397 passwordFileName, accessFileName);
398
399 final JMXServiceURL url = cs.getAddress();
400 log.config("initialize",
401 Agent.getText("jmxremote.ConnectorBootstrap.initialize.ready",
402 new JMXServiceURL(url.getProtocol(),
403 url.getHost(),
404 url.getPort(),
405 "/jndi/rmi://"+url.getHost()+":"+port+"/"+
406 "jmxrmi").toString()));
407 } catch (Exception e) {
408 throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString());
409 }
410 return cs;
411 }
412
413 /*
414 * Creates and starts a RMI Connector Server for "local" monitoring
415 * and management.
416 */
417 public static JMXConnectorServer startLocalConnectorServer() {
418 // Ensure cryptographically strong random number generater used
419 // to choose the object number - see java.rmi.server.ObjID
420 System.setProperty("java.rmi.server.randomIDs", "true");
421
422 // This RMI server should not keep the VM alive
423 Map<String, Object> env = new HashMap<String, Object>();
424 env.put(RMIExporter.EXPORTER_ATTRIBUTE, new PermanentExporter());
425
426 // The local connector server need only be available via the
427 // loopback connection.
428 String localhost = "localhost";
429 InetAddress lh = null;
430 try {
431 lh = InetAddress.getByName(localhost);
432 localhost = lh.getHostAddress();
433 } catch (UnknownHostException x) {
434 }
435
436 // localhost unknown or (somehow) didn't resolve to
437 // a loopback address.
438 if (lh == null || !lh.isLoopbackAddress()) {
439 localhost = "127.0.0.1";
440 }
441
442 MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
443 try {
444 JMXServiceURL url = new JMXServiceURL("rmi", localhost, 0);
445 JMXConnectorServer server =
446 JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
447 server.start();
448 return server;
449 } catch (Exception e) {
450 throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString());
451 }
452 }
453
454 private static void checkPasswordFile(String passwordFileName) {
455 if (passwordFileName == null || passwordFileName.length()==0) {
456 throw new AgentConfigurationError(PASSWORD_FILE_NOT_SET);
457 }
458 File file = new File(passwordFileName);
459 if (!file.exists()) {
460 throw new AgentConfigurationError(PASSWORD_FILE_NOT_FOUND, passwordFileName);
461 }
462
463 if (!file.canRead()) {
464 throw new AgentConfigurationError(PASSWORD_FILE_NOT_READABLE, passwordFileName);
465 }
466
467 FileSystem fs = FileSystem.open();
468 try {
469 if (fs.supportsFileSecurity(file)) {
470 if (!fs.isAccessUserOnly(file)) {
471 final String msg=Agent.getText("jmxremote.ConnectorBootstrap.initialize.password.readonly",
472 passwordFileName);
473 log.config("initialize",msg);
474 throw new AgentConfigurationError(PASSWORD_FILE_ACCESS_NOT_RESTRICTED,
475 passwordFileName);
476 }
477 }
478 } catch (IOException e) {
479 throw new AgentConfigurationError(PASSWORD_FILE_READ_FAILED,
480 e, passwordFileName);
481 }
482 }
483
484 private static void checkAccessFile(String accessFileName) {
485 if (accessFileName == null || accessFileName.length()==0) {
486 throw new AgentConfigurationError(ACCESS_FILE_NOT_SET);
487 }
488 File file = new File(accessFileName);
489 if (!file.exists()) {
490 throw new AgentConfigurationError(ACCESS_FILE_NOT_FOUND, accessFileName);
491 }
492
493 if (!file.canRead()) {
494 throw new AgentConfigurationError(ACCESS_FILE_NOT_READABLE, accessFileName);
495 }
496 }
497
498 private static void checkRestrictedFile(String restrictedFileName) {
499 if (restrictedFileName == null || restrictedFileName.length() == 0) {
500 throw new AgentConfigurationError(FILE_NOT_SET);
501 }
502 File file = new File(restrictedFileName);
503 if (!file.exists()) {
504 throw new AgentConfigurationError(FILE_NOT_FOUND, restrictedFileName);
505 }
506 if (!file.canRead()) {
507 throw new AgentConfigurationError(FILE_NOT_READABLE, restrictedFileName);
508 }
509 FileSystem fs = FileSystem.open();
510 try {
511 if (fs.supportsFileSecurity(file)) {
512 if (!fs.isAccessUserOnly(file)) {
513 final String msg = Agent.getText(
514 "jmxremote.ConnectorBootstrap.initialize.file.readonly",
515 restrictedFileName);
516 log.config("initialize", msg);
517 throw new AgentConfigurationError(
518 FILE_ACCESS_NOT_RESTRICTED, restrictedFileName);
519 }
520 }
521 } catch (IOException e) {
522 throw new AgentConfigurationError(
523 FILE_READ_FAILED, e, restrictedFileName);
524 }
525 }
526
527 /**
528 * Compute the full path name for a default file.
529 * @param basename basename (with extension) of the default file.
530 * @return ${JRE}/lib/management/${basename}
531 **/
532 private static String getDefaultFileName(String basename) {
533 final String fileSeparator = File.separator;
534 return System.getProperty("java.home") + fileSeparator + "lib" +
535 fileSeparator + "management" + fileSeparator +
536 basename;
537 }
538
539 private static SslRMIServerSocketFactory createSslRMIServerSocketFactory(
540 String sslConfigFileName,
541 String[] enabledCipherSuites,
542 String[] enabledProtocols,
543 boolean sslNeedClientAuth) {
544 if (sslConfigFileName == null) {
545 return new SslRMIServerSocketFactory(
546 enabledCipherSuites,
547 enabledProtocols,
548 sslNeedClientAuth);
549 } else {
550 checkRestrictedFile(sslConfigFileName);
551 try {
552 // Load the SSL keystore properties from the config file
553 Properties p = new Properties();
554 InputStream in = new FileInputStream(sslConfigFileName);
555 try {
556 BufferedInputStream bin = new BufferedInputStream(in);
557 p.load(bin);
558 } finally {
559 in.close();
560 }
561 String keyStore =
562 p.getProperty("javax.net.ssl.keyStore");
563 String keyStorePassword =
564 p.getProperty("javax.net.ssl.keyStorePassword", "");
565 String trustStore =
566 p.getProperty("javax.net.ssl.trustStore");
567 String trustStorePassword =
568 p.getProperty("javax.net.ssl.trustStorePassword", "");
569
570 char[] keyStorePasswd = null;
571 if (keyStorePassword.length() != 0) {
572 keyStorePasswd = keyStorePassword.toCharArray();
573 }
574
575 char[] trustStorePasswd = null;
576 if (trustStorePassword.length() != 0) {
577 trustStorePasswd = trustStorePassword.toCharArray();
578 }
579
580 KeyStore ks = null;
581 if (keyStore != null) {
582 ks = KeyStore.getInstance(KeyStore.getDefaultType());
583 FileInputStream ksfis = new FileInputStream(keyStore);
584 try {
585 ks.load(ksfis, keyStorePasswd);
586 } finally {
587 ksfis.close();
588 }
589 }
590 KeyManagerFactory kmf = KeyManagerFactory.getInstance(
591 KeyManagerFactory.getDefaultAlgorithm());
592 kmf.init(ks, keyStorePasswd);
593
594 KeyStore ts = null;
595 if (trustStore != null) {
596 ts = KeyStore.getInstance(KeyStore.getDefaultType());
597 FileInputStream tsfis = new FileInputStream(trustStore);
598 try {
599 ts.load(tsfis, trustStorePasswd);
600 } finally {
601 tsfis.close();
602 }
603 }
604 TrustManagerFactory tmf = TrustManagerFactory.getInstance(
605 TrustManagerFactory.getDefaultAlgorithm());
606 tmf.init((KeyStore) ts);
607
608 SSLContext ctx = SSLContext.getInstance("SSL");
609 ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
610
611 return new SslRMIServerSocketFactory(
612 ctx,
613 enabledCipherSuites,
614 enabledProtocols,
615 sslNeedClientAuth);
616 } catch (Exception e) {
617 throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString());
618 }
619 }
620 }
621
622 private static JMXConnectorServer exportMBeanServer(
623 MBeanServer mbs,
624 int port,
625 boolean useSsl,
626 boolean useRegistrySsl,
627 String sslConfigFileName,
628 String[] enabledCipherSuites,
629 String[] enabledProtocols,
630 boolean sslNeedClientAuth,
631 boolean useAuthentication,
632 String loginConfigName,
633 String passwordFileName,
634 String accessFileName)
635 throws IOException, MalformedURLException {
636
637 /* Make sure we use non-guessable RMI object IDs. Otherwise
638 * attackers could hijack open connections by guessing their
639 * IDs. */
640 System.setProperty("java.rmi.server.randomIDs", "true");
641
642 JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
643
644 Map<String, Object> env = new HashMap<String, Object>();
645
646 PermanentExporter exporter = new PermanentExporter();
647
648 env.put(RMIExporter.EXPORTER_ATTRIBUTE, exporter);
649
650 if (useAuthentication) {
651 if (loginConfigName != null) {
652 env.put("jmx.remote.x.login.config", loginConfigName);
653 }
654 if (passwordFileName != null) {
655 env.put("jmx.remote.x.password.file", passwordFileName);
656 }
657
658 env.put("jmx.remote.x.access.file", accessFileName);
659
660 if (env.get("jmx.remote.x.password.file") != null ||
661 env.get("jmx.remote.x.login.config") != null) {
662 env.put(JMXConnectorServer.AUTHENTICATOR,
663 new AccessFileCheckerAuthenticator(env));
664 }
665 }
666
667 RMIClientSocketFactory csf = null;
668 RMIServerSocketFactory ssf = null;
669
670 if (useSsl || useRegistrySsl) {
671 csf = new SslRMIClientSocketFactory();
672 ssf = createSslRMIServerSocketFactory(
673 sslConfigFileName, enabledCipherSuites,
674 enabledProtocols, sslNeedClientAuth);
675 }
676
677 if (useSsl) {
678 env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
679 csf);
680 env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
681 ssf);
682 }
683
684 JMXConnectorServer connServer = null;
685 try {
686 connServer =
687 JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
688 connServer.start();
689 } catch (IOException e) {
690 if (connServer == null) {
691 throw new AgentConfigurationError(CONNECTOR_SERVER_IO_ERROR,
692 e, url.toString());
693 } else {
694 throw new AgentConfigurationError(CONNECTOR_SERVER_IO_ERROR,
695 e, connServer.getAddress().toString());
696 }
697 }
698
699 final Registry registry;
700 if (useRegistrySsl)
701 registry =
702 new SingleEntryRegistry(port, csf, ssf,
703 "jmxrmi", exporter.firstExported);
704 else
705 registry =
706 new SingleEntryRegistry(port,
707 "jmxrmi", exporter.firstExported);
708
709 /* Our exporter remembers the first object it was asked to
710 export, which will be an RMIServerImpl appropriate for
711 publication in our special registry. We could
712 alternatively have constructed the RMIServerImpl explicitly
713 and then constructed an RMIConnectorServer passing it as a
714 parameter, but that's quite a bit more verbose and pulls in
715 lots of knowledge of the RMI connector. */
716
717 return connServer;
718 }
719
720 /**
721 * This class cannot be instantiated.
722 **/
723 private ConnectorBootstrap() {
724 }
725
726 // XXX Revisit: should probably clone this MibLogger....
727 private static final MibLogger log =
728 new MibLogger(ConnectorBootstrap.class);
729
730}