Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

RmiJmxClient.java

00001 /**
00002  * C-JDBC: Clustered JDBC.
00003  * Copyright (C) 2002-2004 French National Institute For Research In Computer
00004  * Science And Control (INRIA).
00005  * Contact: c-jdbc@objectweb.org
00006  * 
00007  * This library is free software; you can redistribute it and/or modify it
00008  * under the terms of the GNU Lesser General Public License as published by the
00009  * Free Software Foundation; either version 2.1 of the License, or any later
00010  * version.
00011  * 
00012  * This library is distributed in the hope that it will be useful, but WITHOUT
00013  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
00015  * for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public License
00018  * along with this library; if not, write to the Free Software Foundation,
00019  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00020  *
00021  * Initial developer(s): Nicolas Modrzyk
00022  * Contributor(s): ______________________.
00023  */
00024 
00025 package org.objectweb.cjdbc.console.jmx;
00026 
00027 import java.io.IOException;
00028 import java.util.HashMap;
00029 import java.util.HashSet;
00030 import java.util.Map;
00031 import java.util.Set;
00032 
00033 import javax.management.Attribute;
00034 import javax.management.InstanceNotFoundException;
00035 import javax.management.MBeanInfo;
00036 import javax.management.MBeanOperationInfo;
00037 import javax.management.MBeanParameterInfo;
00038 import javax.management.MBeanServerConnection;
00039 import javax.management.MBeanServerInvocationHandler;
00040 import javax.management.NotificationListener;
00041 import javax.management.ObjectName;
00042 import javax.management.monitor.StringMonitor;
00043 import javax.management.remote.JMXConnector;
00044 import javax.management.remote.JMXConnectorFactory;
00045 import javax.management.remote.JMXServiceURL;
00046 import javax.naming.Context;
00047 import javax.security.auth.Subject;
00048 
00049 import org.objectweb.cjdbc.common.exceptions.VirtualDatabaseException;
00050 import org.objectweb.cjdbc.common.jmx.JmxConstants;
00051 import org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean;
00052 import org.objectweb.cjdbc.common.jmx.mbeans.DataCollectorMBean;
00053 import org.objectweb.cjdbc.common.jmx.mbeans.DatabaseBackendMBean;
00054 import org.objectweb.cjdbc.common.jmx.mbeans.VirtualDatabaseMBean;
00055 import org.objectweb.cjdbc.common.users.AdminUser;
00056 import org.objectweb.cjdbc.controller.authentication.PasswordAuthenticator;
00057 
00058 /**
00059  * This class defines a RmiJmxClient that uses Jmx 2.0 specifications to connect
00060  * to the RmiSever
00061  * 
00062  * @author <a href="mailto:Nicolas.Modrzyk@inria.fr">Nicolas Modrzyk </a>
00063  * @version 1.0
00064  */
00065 public class RmiJmxClient
00066 {
00067   private JMXConnector         connector;
00068   private Object               credentials;
00069   private String               remoteHostAddress;
00070   private String               remoteHostPort;
00071 
00072   private NotificationListener notificationListener;
00073 
00074   // List of last used MBeans
00075   private ControllerMBean      controllerMBean;
00076   private VirtualDatabaseMBean virtualDbMBean;
00077   private DatabaseBackendMBean backendMBean;
00078   private DataCollectorMBean   dataMBean;
00079 
00080   /**
00081    * Returns the notificationListener value.
00082    * 
00083    * @return Returns the notificationListener.
00084    */
00085   public NotificationListener getNotificationListener()
00086   {
00087     return notificationListener;
00088   }
00089 
00090   /**
00091    * Sets the notificationListener value.
00092    * 
00093    * @param notificationListener The notificationListener to set.
00094    */
00095   public void setNotificationListener(NotificationListener notificationListener)
00096   {
00097     this.notificationListener = notificationListener;
00098   }
00099 
00100   /**
00101    * Returns the credentials value.
00102    * 
00103    * @return Returns the credentials.
00104    */
00105   public Object getCredentials()
00106   {
00107     return credentials;
00108   }
00109 
00110   /**
00111    * Creates a new <code>RmiJmxClient.java</code> object
00112    * 
00113    * @param port the port of the host to connect to
00114    * @param host the host name to connect to
00115    * @param jmxUser the jmxUser if one, to be authenticated with
00116    * @param jmxPassword the jmxPassword if one, to be authenticated with
00117    * @throws IOException if cannot connect
00118    */
00119   public RmiJmxClient(String port, String host, String jmxUser,
00120       String jmxPassword) throws IOException
00121   {
00122     this(port, host, PasswordAuthenticator.createCredentials(jmxUser,
00123         jmxPassword));
00124   }
00125 
00126   /**
00127    * Creates a new <code>RmiJmxClient</code> object
00128    * 
00129    * @param url the jmx connector url
00130    * @param credentials to use for the connection
00131    * @throws IOException if connect fails
00132    */
00133   public RmiJmxClient(String url, Object credentials) throws IOException
00134   {
00135     int index = url.indexOf(":");
00136     String ip = url.substring(0, index);
00137     String port = url.substring(index + 1);
00138     connect(port, ip, credentials);
00139   }
00140 
00141   /**
00142    * Creates a new <code>RmiJmxClient.java</code> object
00143    * 
00144    * @param port the port of the host to connect to
00145    * @param host the host name to connect to
00146    * @param credentials to use for the connection
00147    * @throws IOException if connect fails
00148    */
00149   public RmiJmxClient(String port, String host, Object credentials)
00150       throws IOException
00151   {
00152     connect(port, host, credentials);
00153   }
00154 
00155   /**
00156    * Connect to the MBean server
00157    * 
00158    * @param port the port of the host to connect to
00159    * @param host the host name to connect to
00160    * @param credentials to use for the connection
00161    * @throws IOException if connect fails
00162    */
00163   public void connect(String port, String host, Object credentials)
00164       throws IOException
00165   {
00166     JMXServiceURL address = new JMXServiceURL("rmi", host, 0, "/jndi/jrmp");
00167 
00168     Map environment = new HashMap();
00169     environment.put(Context.INITIAL_CONTEXT_FACTORY,
00170         "com.sun.jndi.rmi.registry.RegistryContextFactory");
00171     environment.put(Context.PROVIDER_URL, "rmi://" + host + ":" + port);
00172 
00173     // use username and password for authentication of connections
00174     // with the controller, the values are compared to the ones
00175     // specified in the controller.xml config file.
00176     if (credentials != null)
00177     {
00178       // this line is not required if no username/password has been configered
00179       environment.put(JMXConnector.CREDENTIALS, credentials);
00180     }
00181 
00182     this.credentials = credentials;
00183 
00184     connector = JMXConnectorFactory.connect(address, environment);
00185     remoteHostAddress = host;
00186     remoteHostPort = port;
00187   }
00188 
00189   /**
00190    * List of all the mbean on the current server
00191    * 
00192    * @return a set of <tt>ObjectInstance</tt>
00193    * @throws Exception if fails
00194    */
00195   public Set listCJDBCMBeans() throws Exception
00196   {
00197     Set set = connector.getMBeanServerConnection().queryMBeans(
00198         new ObjectName("c-jdbc:*"), null);
00199     return set;
00200   }
00201 
00202   /**
00203    * Get the mbean information
00204    * 
00205    * @param mbean the <tt>ObjectName</tt> of the mbean to access
00206    * @return <tt>MBeanInfo</tt> object
00207    * @throws Exception if fails
00208    */
00209   public MBeanInfo getMBeanInfo(ObjectName mbean) throws Exception
00210   {
00211     return connector.getMBeanServerConnection().getMBeanInfo(mbean);
00212   }
00213 
00214   /**
00215    * Get the value of an attribute on the given mbean
00216    * 
00217    * @param mbean the <tt>ObjectName</tt> of the mbean to access
00218    * @param attribute the attribute name
00219    * @return <tt>Object</tt> being the value returned by the get <Attribute>
00220    *         method
00221    * @throws Exception if fails
00222    */
00223   public Object getAttributeValue(ObjectName mbean, String attribute)
00224       throws Exception
00225   {
00226     return connector.getMBeanServerConnection().getAttribute(mbean, attribute);
00227   }
00228 
00229   /**
00230    * Change an attribute value
00231    * 
00232    * @param mbean the <tt>ObjectName</tt> of the mbean to access
00233    * @param attribute the attribute name
00234    * @param value the attribute new value
00235    * @throws Exception if fails
00236    */
00237   public void setAttributeValue(ObjectName mbean, String attribute, Object value)
00238       throws Exception
00239   {
00240     Attribute att = new Attribute(attribute, value);
00241     connector.getMBeanServerConnection().setAttribute(mbean, att);
00242   }
00243 
00244   /**
00245    * Set the current subject for authentication
00246    * 
00247    * @param user the user login
00248    * @param password the user password
00249    */
00250   public void setCurrentSubject(String user, String password)
00251   {
00252     if (user != null && password != null)
00253     {
00254       // we build a subject for authentication
00255       AdminUser dbUser = new AdminUser(user, password);
00256       Set principals = new HashSet();
00257       principals.add(dbUser);
00258       subject = new Subject(true, principals, new HashSet(), new HashSet());
00259     }
00260   }
00261 
00262   Subject subject;
00263 
00264   /**
00265    * Has the subject been set?
00266    * 
00267    * @return true if the subject is not null
00268    */
00269   public boolean isSubjectSet()
00270   {
00271     return subject != null;
00272   }
00273 
00274   /**
00275    * Invoke an operation on the given object.
00276    * 
00277    * @param name object name
00278    * @param operation operation to invoke
00279    * @param args method arguments
00280    * @return result of operation invocation
00281    * @throws Exception if an error occurs
00282    */
00283   public Object invokeOperation(ObjectName name, MBeanOperationInfo operation,
00284       Object[] args) throws Exception
00285   {
00286     if (JmxConstants.mbeanNeedAuthentication(name))
00287     {
00288       if (!isSubjectSet())
00289         throw new Exception(
00290             "Subject has not been set for this jmx client, and authentication is required");
00291       return connector.getMBeanServerConnection(subject).invoke(name,
00292           operation.getName(), args, getSignature(operation));
00293     }
00294     else
00295     {
00296       return connector.getMBeanServerConnection().invoke(name,
00297           operation.getName(), args, getSignature(operation));
00298     }
00299   }
00300 
00301   private String[] getSignature(MBeanOperationInfo operation)
00302   {
00303     MBeanParameterInfo[] info = operation.getSignature();
00304     String[] signature = new String[info.length];
00305     for (int i = 0; i < info.length; i++)
00306       signature[i] = info[i].getType();
00307     return signature;
00308   }
00309 
00310   /**
00311    * Get a reference to the virtualdatabaseMbean with the given authentication
00312    * 
00313    * @param database the virtual database name
00314    * @param user the user recognized as the <code>VirtualDatabaseUser</code>
00315    * @param password the password for the <code>VirtualDatabaseUser</code>
00316    * @return <code>VirtualDatabaseMBean</code> instance
00317    * @throws IOException if cannot connect to MBean
00318    * @throws InstanceNotFoundException if cannot locate MBean
00319    * @throws VirtualDatabaseException if virtual database fails
00320    */
00321   public VirtualDatabaseMBean getVirtualDatabaseProxy(String database,
00322       String user, String password) throws InstanceNotFoundException,
00323       IOException, VirtualDatabaseException
00324   {
00325     if (virtualDbMBean != null && isValidConnection()
00326         && virtualDbMBean.getVirtualDatabaseName().equals(database))
00327     {
00328       return virtualDbMBean;
00329     }
00330     else
00331     {
00332       ObjectName db = JmxConstants.getVirtualDbObjectName(database);
00333 
00334       // we build a subject for authentication
00335       AdminUser dbUser = new AdminUser(user, password);
00336       Set principals = new HashSet();
00337       principals.add(dbUser);
00338       Subject subj = new Subject(true, principals, new HashSet(), new HashSet());
00339 
00340       // we open a connection for this subject, all subsequent calls with this
00341       // connection will be executed on the behalf of our subject.
00342       MBeanServerConnection delegateConnection = connector
00343           .getMBeanServerConnection(subj);
00344 
00345       // we create a proxy to the virtual database
00346       VirtualDatabaseMBean local = (VirtualDatabaseMBean) MBeanServerInvocationHandler
00347           .newProxyInstance(delegateConnection, db, VirtualDatabaseMBean.class,
00348               false);
00349 
00350       // Check authentication
00351       boolean authenticated = false;
00352       try
00353       {
00354         authenticated = local.checkAdminAuthentication(user, password);
00355       }
00356       catch (Exception e)
00357       {
00358         throw new VirtualDatabaseException(
00359             "Could not check authentication. MBean is not accessible.");
00360       }
00361       if (!authenticated)
00362         throw new VirtualDatabaseException("Authentication Failed");
00363 
00364       // Add notification listener
00365       if (notificationListener != null)
00366       {
00367         delegateConnection.addNotificationListener(db, notificationListener,
00368             null, null);
00369 
00370         // CounterMonitor cm = new CounterMonitor();
00371         // cm.setNotify(true);
00372         // cm.setGranularityPeriod(100);
00373         // cm.setObservedObject(db);
00374         // cm.setObservedAttribute("currentNbOfThreads");
00375         // cm.setThreshold(new Integer(6));
00376         // cm.start();
00377       }
00378 
00379       this.virtualDbMBean = local;
00380 
00381       return virtualDbMBean;
00382     }
00383   }
00384 
00385   /**
00386    * Get a proxy to the ControllerMBean
00387    * 
00388    * @return <code>ControllerMBean</code> instance
00389    * @throws IOException if cannot connect to MBean
00390    */
00391   public ControllerMBean getControllerProxy() throws IOException
00392   {
00393 
00394     if (controllerMBean != null && isValidConnection())
00395     {
00396       return controllerMBean;
00397     }
00398     else
00399     {
00400       if (!isValidConnection())
00401         reconnect();
00402 
00403       ObjectName db = JmxConstants.getControllerObjectName();
00404 
00405       // we create a new proxy to the controller
00406       controllerMBean = (ControllerMBean) MBeanServerInvocationHandler
00407           .newProxyInstance(connector.getMBeanServerConnection(), db,
00408               ControllerMBean.class, false);
00409 
00410       // Add notification listener
00411       if (notificationListener != null)
00412       {
00413         try
00414         {
00415           connector.getMBeanServerConnection().addNotificationListener(db,
00416               notificationListener, null, null);
00417         }
00418         catch (Exception e)
00419         {
00420           throw new IOException("Could not register listener on the mbean");
00421         }
00422       }
00423 
00424       return controllerMBean;
00425     }
00426   }
00427 
00428   /**
00429    * Get a proxy to the DataCollectorMBean
00430    * 
00431    * @return <code>DataCollectorMBean</code> instance
00432    * @throws IOException if fails
00433    */
00434   public DataCollectorMBean getDataCollectorProxy() throws IOException
00435   {
00436 
00437     if (dataMBean != null && isValidConnection())
00438     {
00439       return dataMBean;
00440     }
00441     else
00442     {
00443       if (!isValidConnection())
00444         reconnect();
00445       ObjectName db = JmxConstants.getDataCollectorObjectName();
00446 
00447       // we create a new proxy to the data collector
00448       dataMBean = (DataCollectorMBean) MBeanServerInvocationHandler
00449           .newProxyInstance(connector.getMBeanServerConnection(), db,
00450               DataCollectorMBean.class, false);
00451       return dataMBean;
00452     }
00453   }
00454 
00455   /**
00456    * Get a proxy to the DatabaseBackendMBean
00457    * 
00458    * @return <code>DatabaseBackendMBean</code> instance
00459    * @param vdb virtual database name
00460    * @param backend backend name
00461    * @param user user name
00462    * @param password password name
00463    * @throws IOException if cannot connect to MBean
00464    * @throws InstanceNotFoundException if cannot locate MBean
00465    */
00466   public DatabaseBackendMBean getDatabaseBackendProxy(String vdb,
00467       String backend, String user, String password)
00468       throws InstanceNotFoundException, IOException
00469   {
00470     if (backendMBean != null && isValidConnection())
00471     {
00472       try
00473       {
00474         if (backendMBean.getName().equals(backend))
00475           return backendMBean;
00476       }
00477       catch (Exception e)
00478       {
00479         // backend is no more there
00480       }
00481     }
00482 
00483     if (!isValidConnection())
00484       reconnect();
00485 
00486     // we build a subject for authentication
00487     AdminUser dbUser = new AdminUser(user, password);
00488     Set principals = new HashSet();
00489     principals.add(dbUser);
00490     Subject subj = new Subject(true, principals, new HashSet(), new HashSet());
00491 
00492     ObjectName db = JmxConstants.getDatabaseBackendObjectName(vdb, backend);
00493     MBeanServerConnection delegateConnection = connector
00494         .getMBeanServerConnection(subj);
00495 
00496     if (notificationListener != null)
00497     {
00498       delegateConnection.addNotificationListener(db, notificationListener,
00499           null, null);
00500       StringMonitor sm = new StringMonitor();
00501       sm.setObservedObject(db);
00502       sm.setObservedAttribute("LastKnownCheckpoint");
00503       sm.setStringToCompare("hello");
00504       sm.setGranularityPeriod(100);
00505       sm.setNotifyDiffer(true);
00506       sm.addNotificationListener(notificationListener, null, null);
00507       sm.start();
00508     }
00509 
00510     // we create a proxy to the database backend
00511     backendMBean = (DatabaseBackendMBean) MBeanServerInvocationHandler
00512         .newProxyInstance(delegateConnection, db, DatabaseBackendMBean.class,
00513             false);
00514     return backendMBean;
00515   }
00516 
00517   /**
00518    * Get the controller name used for jmx connection This is
00519    * [hostname]:[jmxServerPort]
00520    * 
00521    * @return <code>remoteHostName+":"+remoteHostPort</code>
00522    */
00523   public String getRemoteName()
00524   {
00525     return remoteHostAddress + ":" + remoteHostPort;
00526   }
00527 
00528   /**
00529    * Returns the remoteHostAddress value.
00530    * 
00531    * @return Returns the remoteHostAddress.
00532    */
00533   public String getRemoteHostAddress()
00534   {
00535     return remoteHostAddress;
00536   }
00537 
00538   /**
00539    * Returns the remoteHostPort value.
00540    * 
00541    * @return Returns the remoteHostPort.
00542    */
00543   public String getRemoteHostPort()
00544   {
00545     return remoteHostPort;
00546   }
00547 
00548   /**
00549    * Reconnect to the same mbean server
00550    * 
00551    * @throws IOException if reconnection failed
00552    */
00553   public void reconnect() throws IOException
00554   {
00555     connect(remoteHostPort, remoteHostAddress, credentials);
00556   }
00557 
00558   /**
00559    * Test if the connection with the mbean server is still valid
00560    * 
00561    * @return true if it is
00562    */
00563   public boolean isValidConnection()
00564   {
00565     try
00566     {
00567       connector.getMBeanServerConnection().getMBeanCount();
00568       return true;
00569     }
00570     catch (Exception e)
00571     {
00572       controllerMBean = null;
00573       backendMBean = null;
00574       virtualDbMBean = null;
00575       dataMBean = null;
00576       return false;
00577     }
00578   }
00579 }

Generated on Mon Apr 11 22:01:34 2005 for C-JDBC by  doxygen 1.3.9.1