001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 028 package org.opends.server.protocols.jmx; 029 030 import java.io.IOException; 031 import java.util.Map; 032 import javax.management.ListenerNotFoundException; 033 import javax.management.MBeanServerConnection; 034 import javax.management.NotificationFilter; 035 import javax.management.NotificationListener; 036 import javax.management.remote.JMXConnector; 037 import javax.management.remote.JMXConnectorFactory; 038 import javax.management.remote.JMXServiceURL; 039 import javax.security.auth.Subject; 040 041 /** 042 * Wrapper class for the JMX's RMI connector. This class has the exact same 043 * functionnalities but maintain inner variables which are used during the 044 * connection phase. 045 * <p> 046 * Note that the javadoc has been copied from the 047 * javax.management.remote.JMXConnector interface. 048 */ 049 public class OpendsJmxConnector implements JMXConnector 050 051 { 052 053 /** 054 * the wrapped JMX RMI connector. 055 */ 056 private JMXConnector jmxc = null; 057 058 /** 059 * the connection environment set at creation. 060 */ 061 private Map<String,Object> environment = null; 062 063 /** 064 * the JMX Service URL. 065 */ 066 private JMXServiceURL serviceURL = null; 067 068 /** 069 * the host to connect to. 070 */ 071 private String serverHostname = null; 072 073 074 075 /** 076 * Creates a connector client for the connector server at the 077 * given host and port. The resultant client is not connected until its 078 * connect method is called. 079 * 080 * @param serverHostname the target server hostname 081 * 082 * @param serverPort the target server port 083 * 084 * @param environment a set of attributes to determine how the 085 * connection is made. This parameter can be null. Keys in this 086 * map must be Strings. The appropriate type of each associated 087 * value depends on the attribute. The contents of 088 * <code>environment</code> are not changed by this call. 089 * 090 * @exception IOException if the connector client cannot be made 091 * because of a communication problem. 092 * 093 */ 094 public OpendsJmxConnector(String serverHostname, int serverPort, 095 Map<String,Object> environment) throws IOException 096 { 097 serviceURL = new JMXServiceURL( 098 "service:jmx:rmi:///jndi/rmi://" + serverHostname + ":" + serverPort 099 + "/org.opends.server.protocols.jmx.client-unknown"); 100 101 this.jmxc = JMXConnectorFactory.newJMXConnector(serviceURL, environment); 102 this.serverHostname = serverHostname; 103 this.environment = environment ; 104 } 105 // /** 106 // * Sets this connector's connection environment. 107 // * 108 // * @param environment the new connection env 109 // */ 110 // public void setConnectionEnv(Map connectionEnv) 111 // { 112 // this.environment = environment; 113 // } 114 115 /** 116 * Returns the connection environment. 117 * 118 * @return Map the connection environment used by new connections 119 */ 120 public Map getConnectionEnv() 121 { 122 return environment; 123 } 124 125 /** 126 * Establishes the connection to the connector server. This method is 127 * equivalent to connect(null). 128 * 129 * @throws IOException 130 * if the connection could not be made because of a communication 131 * problem. 132 * @throws SecurityException 133 * if the connection could not be made for security reasons. 134 */ 135 public void connect() throws IOException, SecurityException 136 { 137 this.connect(null); 138 } 139 140 /** 141 * Establishes the connection to the connector server. If connect has 142 * already been called successfully on this object, calling it again has 143 * no effect. If, however, close() was called after connect, the new 144 * connect will throw an IOException. Otherwise, either connect has never 145 * been called on this object, or it has been called but produced an 146 * exception. Then calling connect will attempt to establish a connection 147 * to the connector server. 148 * 149 * @param env 150 * the properties of the connection. Properties in this map 151 * override properties in the map specified when the JMXConnector 152 * was created, if any. This parameter can be null, which is 153 * equivalent to an empty map. 154 * @throws IOException 155 * if the connection could not be made because of a communication 156 * problem. 157 * @throws SecurityException - 158 * if the connection could not be made for security reasons. 159 */ 160 public void connect(Map<String,?> env) throws IOException, SecurityException 161 { 162 // set the real target hostname 163 DirectoryRMIClientSocketFactory.setServerHostname(serverHostname); 164 165 // configure the thread-local connection environment 166 if (env != null) 167 { 168 // encode credentials if necessary 169 updateCredentials(env); 170 } 171 DirectoryRMIClientSocketFactory.setConnectionEnv(environment); 172 173 174 jmxc.connect(env); 175 } 176 177 /** 178 * Returns an MBeanServerConnection object representing a remote MBean 179 * server. For a given JMXConnector, two successful calls to this method 180 * will usually return the same MBeanServerConnection object, though this 181 * is not required. For each method in the returned 182 * MBeanServerConnection, calling the method causes the corresponding 183 * method to be called in the remote MBean server. The value returned by 184 * the MBean server method is the value returned to the client. If the 185 * MBean server method produces an Exception, the same Exception is seen 186 * by the client. If the MBean server method, or the attempt to call it, 187 * produces an Error, the Error is wrapped in a JMXServerErrorException, 188 * which is seen by the client. Calling this method is equivalent to 189 * calling getMBeanServerConnection(null) meaning that no delegation 190 * subject is specified and that all the operations called on the 191 * MBeanServerConnection must use the authenticated subject, if any. 192 * 193 * @return an object that implements the MBeanServerConnection interface 194 * by forwarding its methods to the remote MBean server. 195 * @throws IOException - 196 * if a valid MBeanServerConnection cannot be created, for 197 * instance because the connection to the remote MBean server has 198 * not yet been established (with the connect method), or it has 199 * been closed, or it has broken. 200 */ 201 public MBeanServerConnection getMBeanServerConnection() throws IOException 202 { 203 return jmxc.getMBeanServerConnection(); 204 } 205 206 /** 207 * Returns an MBeanServerConnection object representing a remote MBean 208 * server on which operations are performed on behalf of the supplied 209 * delegation subject. For a given JMXConnector and Subject, two 210 * successful calls to this method will usually return the same 211 * MBeanServerConnection object, though this is not required. For each 212 * method in the returned MBeanServerConnection, calling the method 213 * causes the corresponding method to be called in the remote MBean 214 * server on behalf of the given delegation subject instead of the 215 * authenticated subject. The value returned by the MBean server method 216 * is the value returned to the client. If the MBean server method 217 * produces an Exception, the same Exception is seen by the client. If 218 * the MBean server method, or the attempt to call it, produces an Error, 219 * the Error is wrapped in a JMXServerErrorException, which is seen by 220 * the client. 221 * 222 * @param delegationSubject 223 * the Subject on behalf of which requests will be performed. Can 224 * be null, in which case requests will be performed on behalf of 225 * the authenticated Subject, if any. 226 * @return an object that implements the MBeanServerConnection interface 227 * by forwarding its methods to the remote MBean server on behalf 228 * of a given delegation subject. 229 * @throws IOException 230 * if a valid MBeanServerConnection cannot be created, for 231 * instance because the connection to the remote MBean server has 232 * not yet been established (with the connect method), or it has 233 * been closed, or it has broken. 234 */ 235 public MBeanServerConnection getMBeanServerConnection( 236 Subject delegationSubject) throws IOException 237 { 238 return jmxc.getMBeanServerConnection(delegationSubject); 239 } 240 241 /** 242 * Closes the client connection to its server. Any ongoing or new request 243 * using the MBeanServerConnection returned by getMBeanServerConnection() 244 * will get an IOException. If close has already been called successfully 245 * on this object, calling it again has no effect. If close has never 246 * been called, or if it was called but produced an exception, an attempt 247 * will be made to close the connection. This attempt can succeed, in 248 * which case close will return normally, or it can generate an 249 * exception. Closing a connection is a potentially slow operation. For 250 * example, if the server has crashed, the close operation might have to 251 * wait for a network protocol timeout. Callers that do not want to block 252 * in a close operation should do it in a separate thread. 253 * 254 * @throws IOException 255 * if the connection cannot be closed cleanly. If this exception 256 * is thrown, it is not known whether the server end of the 257 * connection has been cleanly closed. 258 */ 259 public void close() throws IOException 260 { 261 jmxc.close(); 262 } 263 264 /** 265 * Adds a listener to be informed of changes in connection status. The 266 * listener will receive notifications of type JMXConnectionNotification. 267 * An implementation can send other types of notifications too. Any 268 * number of listeners can be added with this method. The same listener 269 * can be added more than once with the same or different values for the 270 * filter and handback. There is no special treatment of a duplicate 271 * entry. For example, if a listener is registered twice with no filter, 272 * then its handleNotification method will be called twice for each 273 * notification. 274 * 275 * @param listener 276 * a listener to receive connection status notifications. 277 * @param filter 278 * a filter to select which notifications are to be delivered to 279 * the listener, or null if all notifications are to be delivered. 280 * @param handback 281 * an object to be given to the listener along with each 282 * notification. Can be null. 283 * @throws NullPointerException 284 * if listener is null. 285 */ 286 public void addConnectionNotificationListener( 287 NotificationListener listener, NotificationFilter filter, 288 Object handback) throws NullPointerException 289 { 290 jmxc.addConnectionNotificationListener(listener, filter, handback); 291 } 292 293 /** 294 * Removes a listener from the list to be informed of changes in status. 295 * The listener must previously have been added. If there is more than 296 * one matching listener, all are removed. 297 * 298 * @param listener - 299 * a listener to receive connection status notifications. 300 * @throws NullPointerException 301 * if listener is null. 302 * @throws ListenerNotFoundException 303 * if the listener is not registered with this JMXConnector. 304 */ 305 public void removeConnectionNotificationListener( 306 NotificationListener listener) throws ListenerNotFoundException, 307 NullPointerException 308 { 309 jmxc.removeConnectionNotificationListener(listener); 310 } 311 312 /** 313 * Removes a listener from the list to be informed of changes in status. 314 * The listener must previously have been added with the same three 315 * parameters. If there is more than one matching listener, only one is 316 * removed. 317 * 318 * @param l 319 * a listener to receive connection status notifications. 320 * @param f 321 * a filter to select which notifications are to be delivered to 322 * the listener. Can be null. handback - an object to be given to 323 * the listener along with each notification. Can be null. 324 * @param handback 325 * an object to be given to the listener along with each 326 * notification. Can be null. 327 * @throws ListenerNotFoundException 328 * if the listener is not registered with this JMXConnector, or 329 * is not registered with the given filter and handback. 330 */ 331 public void removeConnectionNotificationListener( 332 NotificationListener l, NotificationFilter f, Object handback) 333 throws ListenerNotFoundException 334 { 335 jmxc.removeConnectionNotificationListener(l, f, handback); 336 } 337 338 /** 339 * Gets this connection's ID from the connector server. For a given 340 * connector server, every connection will have a unique id which does 341 * not change during the lifetime of the connection. 342 * 343 * @return the unique ID of this connection. This is the same as the ID 344 * that the connector server includes in its 345 * JMXConnectionNotifications. The package description describes 346 * the conventions for connection IDs. 347 * @throws IOException 348 * if the connection ID cannot be obtained, for instance because 349 * the connection is closed or broken. 350 */ 351 public String getConnectionId() throws IOException 352 { 353 return jmxc.getConnectionId(); 354 } 355 356 /** 357 * Update if necessary the credentials of the given map using 358 * information coming from the map given when the connector was created. 359 * This method is called from the connect method when it has received 360 * a non null map holding potentially new credentials. It calls this 361 * method BEFORE actually trying to connect to the server. 362 * 363 * @param Map given to the connect method 364 */ 365 private void updateCredentials(Map env) throws IOException 366 { 367 // credential to update ?? 368 if (!env.containsKey(JMXConnector.CREDENTIALS)) 369 { 370 // NO : nothing to update 371 return; 372 } 373 else 374 { 375 Object cred = env.get(JMXConnector.CREDENTIALS); 376 environment.put(JMXConnector.CREDENTIALS, cred); 377 } 378 } 379 }