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    }