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 }