001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999-2005 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: JmxServiceImpl.java 9313 2006-08-02 13:20:31Z durieuxp $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas.jmx;
025:
026: import java.lang.reflect.Method;
027: import java.net.URI;
028: import java.util.Map;
029: import java.util.Properties;
030:
031: import javax.management.MBeanServerConnection;
032: import javax.management.ObjectName;
033: import javax.management.remote.JMXConnectorServer;
034: import javax.management.remote.JMXConnectorServerFactory;
035: import javax.management.remote.JMXServiceURL;
036: import javax.naming.Context;
037: import javax.naming.InitialContext;
038:
039: import org.objectweb.carol.rmi.util.PortNumber;
040: import org.objectweb.carol.util.configuration.CarolDefaultValues;
041: import org.objectweb.carol.util.configuration.ConfigurationRepository;
042: import org.objectweb.carol.util.configuration.ProtocolConfiguration;
043:
044: import org.objectweb.jonas.common.Log;
045: import org.objectweb.jonas.management.j2eemanagement.J2EEDomain;
046: import org.objectweb.jonas.service.JonasAlreadyStartedException;
047: import org.objectweb.jonas.service.ServiceException;
048:
049: import org.objectweb.util.monolog.api.BasicLevel;
050: import org.objectweb.util.monolog.api.Logger;
051:
052: /**
053: * JMX Service implementation. Provides specific doStart() and doStop () methods
054: * to start /stop JOnAS JMX Service.
055: * @author Adriana Danes.
056: */
057: public class JmxServiceImpl extends AbsJmxServiceImpl {
058:
059: /**
060: * MX4J CommonsLogger fully qualified Classname
061: */
062: private static final String MX4J_COMMONS_LOGGER_CLASSNAME = "mx4j.log.CommonsLogger";
063:
064: /**
065: * MX4J Log class
066: */
067: private static final String MX4J_LOG_CLASSNAME = "mx4j.log.Log";
068:
069: /**
070: * MX4J Logger class
071: */
072: private static final String MX4J_LOGGER_CLASS = "mx4j.log.Logger";
073:
074: /**
075: * The JNDI name of the JOnAS RMI connector allowing to access the
076: * MBeanServer remotely. Use of this connector was needed before the
077: * definition of the Connectors in JMX (before JMX 1.2)
078: */
079: private String rmiConnectorName = null;
080:
081: /**
082: * @return The JNDI name of the JOnAS RMI connector
083: */
084: public String getRmiConnectorName() {
085: return this .rmiConnectorName;
086: }
087:
088: /**
089: * Connector servers attached to the MBean server. We may have several
090: * connector servers only if several communication protocols are used, as
091: * defined by the carol configuration.
092: */
093: private JMXConnectorServer[] connectorServers = null;
094:
095: /**
096: * The JMXServiceURLs the connector servers are actually listening on.
097: */
098: private JMXServiceURL[] connectorServerURLs = null;
099:
100: /**
101: * Management loggers
102: */
103: private static Logger logger = Log.getLogger(Log.JONAS_JMX_PREFIX);
104:
105: /**
106: * Init the logger and then use super method.
107: * @param ctx configuration for the init method
108: * @throws ServiceException if initialization failed
109: */
110: public void doInit(Context ctx) throws ServiceException {
111: // Test if MX4J CommonsLoggger class is present.
112: // In this case, redirect MX4J logging to Jakarta Commons Logging.
113: Class mx4jCommonsLoggerClass = null;
114: try {
115: mx4jCommonsLoggerClass = Thread.currentThread()
116: .getContextClassLoader().loadClass(
117: MX4J_COMMONS_LOGGER_CLASSNAME);
118: if (logger.isLoggable(BasicLevel.DEBUG)) {
119: logger.log(BasicLevel.DEBUG, "Class "
120: + MX4J_COMMONS_LOGGER_CLASSNAME + " founded");
121: }
122: Object o = mx4jCommonsLoggerClass.newInstance();
123:
124: // Load Log a Logger class
125: Class clazz = Thread.currentThread()
126: .getContextClassLoader().loadClass(
127: MX4J_LOG_CLASSNAME);
128: Class mx4jLoggerClass = Thread.currentThread()
129: .getContextClassLoader().loadClass(
130: MX4J_LOGGER_CLASS);
131:
132: // Then get method redirectTo
133: Method m = clazz.getMethod("redirectTo",
134: new Class[] { mx4jLoggerClass });
135: m.invoke(clazz, new Object[] { o });
136: if (logger.isLoggable(BasicLevel.DEBUG)) {
137: logger
138: .log(BasicLevel.DEBUG,
139: "MX4J logging redirected to the Jakarta commons logger");
140: }
141: } catch (ClassNotFoundException cnfe) {
142: if (logger.isLoggable(BasicLevel.DEBUG)) {
143: logger.log(BasicLevel.DEBUG, "Class "
144: + MX4J_COMMONS_LOGGER_CLASSNAME
145: + " not found: " + cnfe);
146: }
147: } catch (Exception e) {
148: if (logger.isLoggable(BasicLevel.WARN)) {
149: logger.log(BasicLevel.WARN, "Problem with "
150: + MX4J_COMMONS_LOGGER_CLASSNAME
151: + " instance creation " + e);
152: }
153: }
154: super .doInit(ctx);
155: }
156:
157: /**
158: * Start the Service. Only need to create a RMI connector
159: * @exception ServiceException the service could not be started
160: */
161: public void doStart() throws ServiceException {
162: String serverName = getJonasServerName();
163: try {
164: // Create a RMI Connector for the JMX agent using the JOnAS
165: // RMIConnector class
166: // --------------------------------------------------------------------------
167: RMIConnector rmiConnector = new RMIConnectorImpl(
168: getJmxServer());
169: // Register the connector in JNDI
170: InitialContext ictx = new InitialContext();
171: rmiConnectorName = "RMIConnector_" + serverName;
172: ictx.rebind(rmiConnectorName, rmiConnector);
173: ictx.close();
174:
175: // Create one or more connector servers
176: // (cf. JSR 160, JMX Remote 1.0)
177: // Create a JMXServiceURL and a JMXConnectorServer per protocol
178: // As some protocols does not have associated connectors, the
179: // 2 arrays below may have null values
180: // ------------------------------------
181: int nbProtocols = ConfigurationRepository
182: .getActiveConfigurationsNumber();
183: connectorServerURLs = new JMXServiceURL[nbProtocols];
184: connectorServers = new JMXConnectorServer[nbProtocols];
185: // Determine protocols used by Carol and their configuration
186: ProtocolConfiguration[] protocolConfigurations = ConfigurationRepository
187: .getConfigurations();
188: String serviceURL = null;
189: for (int i = 0; i < protocolConfigurations.length; i++) {
190: ProtocolConfiguration protocolConfiguration = protocolConfigurations[i];
191: ConfigurationRepository
192: .setCurrentConfiguration(protocolConfiguration);
193: String carolProtocol = protocolConfiguration.getName();
194: String providerUrl = protocolConfiguration
195: .getProviderURL();
196: URI carolURL = new URI(providerUrl);
197: String host = carolURL.getHost();
198: String port = String.valueOf(carolURL.getPort());
199: String scheme = carolURL.getScheme();
200: String ictxFactory = protocolConfiguration
201: .getProtocol()
202: .getInitialContextFactoryClassName();
203: Properties props = new Properties();
204: if (scheme.equals("rmi")
205: && carolProtocol.equals("jrmp")) {
206: // Treat RMI/JRMP cas
207: String myName = "jrmpconnector_" + serverName;
208: serviceURL = "service:jmx:rmi://" + host;
209: int jrmpExportedPort = 0;
210: // Add port number of exported objects if one is set by
211: // carol.
212: String propertyName = CarolDefaultValues.SERVER_JRMP_PORT;
213: Properties p = ConfigurationRepository
214: .getProperties();
215: if (p != null) {
216: jrmpExportedPort = PortNumber.strToint(p
217: .getProperty(propertyName, "0"),
218: propertyName);
219: }
220: if (jrmpExportedPort > 0) {
221: serviceURL += ":" + jrmpExportedPort;
222: }
223: serviceURL += "/jndi/rmi://" + host + ":" + port
224: + "/" + myName;
225:
226: props.put(Context.INITIAL_CONTEXT_FACTORY,
227: ictxFactory);
228: props.put(Context.PROVIDER_URL, providerUrl);
229: } else if (scheme.equals("rmi")
230: && carolProtocol.equals("irmi")) {
231: // Treat RMI/IRMI cas
232: String myName = "irmiconnector_" + serverName;
233: serviceURL = "service:jmx:rmi://" + host;
234: int irmiExportedPort = 0;
235: // Add port number of exported objects if one is set by
236: // carol.
237: String propertyName = CarolDefaultValues.SERVER_IRMI_PORT;
238: Properties p = ConfigurationRepository
239: .getProperties();
240: if (p != null) {
241: irmiExportedPort = PortNumber.strToint(p
242: .getProperty(propertyName, "0"),
243: propertyName);
244: // Add 1 to this port for IRMI as the JMX object will
245: // not use IRMI to bind but JRMP methods.
246: irmiExportedPort++;
247: }
248: if (irmiExportedPort > 1) {
249: serviceURL += ":" + irmiExportedPort;
250: }
251: serviceURL += "/jndi/rmi://" + host + ":" + port
252: + "/" + myName;
253: props.put(Context.INITIAL_CONTEXT_FACTORY,
254: ictxFactory);
255: props.put(Context.PROVIDER_URL, providerUrl);
256: } else if (scheme.equals("jrmi")) { // Treat JEREMIE case
257: String myName = "jeremieconnector_" + serverName;
258: serviceURL = "service:jmx:rmi://" + host;
259: int jeremieExportedPort = 0;
260: // Add port number of exported objects if one is set by
261: // carol.
262: String propertyName = CarolDefaultValues.SERVER_JEREMIE_PORT;
263: Properties p = ConfigurationRepository
264: .getProperties();
265: if (p != null) {
266: jeremieExportedPort = PortNumber.strToint(p
267: .getProperty(propertyName, "0"),
268: propertyName);
269: // Add 1 to this port for jeremie as the JMX object will
270: // not use jeremie to bind but JRMP methods.
271: jeremieExportedPort++;
272: }
273: if (jeremieExportedPort > 1024) {
274: serviceURL += ":" + jeremieExportedPort;
275: }
276: serviceURL += "/jndi/jrmi://" + host + ":" + port
277: + "/" + myName;
278:
279: props.put(Context.INITIAL_CONTEXT_FACTORY,
280: ictxFactory);
281: props.put(Context.PROVIDER_URL, providerUrl);
282: } else if (scheme.equals("cmi")) {
283: // Treat CMI case
284: String myName = "cmiconnector_" + serverName;
285: serviceURL = "service:jmx:rmi://" + host;
286:
287: int jrmpExportedPort = 0;
288: // Add port number of exported objects if one is set by
289: // carol.
290: String propertyName = CarolDefaultValues.SERVER_JRMP_PORT;
291: Properties p = ConfigurationRepository
292: .getProperties();
293: if (p != null) {
294: jrmpExportedPort = PortNumber.strToint(p
295: .getProperty(propertyName, "0"),
296: propertyName);
297: }
298: if (jrmpExportedPort > 0) {
299: serviceURL += ":" + jrmpExportedPort;
300: }
301: serviceURL += "/jndi/cmi://" + host + ":" + port
302: + "/" + myName;
303:
304: props.put(Context.INITIAL_CONTEXT_FACTORY,
305: ictxFactory);
306: props.put(Context.PROVIDER_URL, providerUrl);
307:
308: } else if (scheme.equals("iiop")) {
309: // Treat RMI/IIOP case
310: String myName = "iiopconnector_" + serverName;
311: // serviceURL = "service:jmx:iiop://" + host + "/jndi/" +
312: // myName;
313:
314: serviceURL = "service:jmx:iiop://" + host
315: + "/jndi/iiop://" + host + ":" + port + "/"
316: + myName;
317:
318: props.put("java.naming.corba.orb",
319: new InitialContext()
320: .lookup("java:comp/ORB"));
321: } else {
322: // Currently do not create connectors for other protocols.
323: connectorServerURLs[i] = null;
324: continue;
325: }
326:
327: JMXServiceURL url = new JMXServiceURL(serviceURL);
328: // - Cast to Map is required for JDK 1.5
329: // - Set the "jmx.remote.jndi.rebind" to ask the JMX impl to do
330: // a rebind() rather than a bind() in the registry
331: props.put("jmx.remote.jndi.rebind", "true");
332:
333: JMXConnectorServer connectorServer = JMXConnectorServerFactory
334: .newJMXConnectorServer(url, (Map) props, null);
335: connectorServers[i] = connectorServer;
336: // Create the MBean associated to the connector
337: String connectorObjectName = "connector_"
338: + carolProtocol;
339: ObjectName connectorServerName = JonasObjectName
340: .jmxConnectorServer(scheme, connectorObjectName);
341: getJmxServer().registerMBean(connectorServer,
342: connectorServerName);
343: // Start the JMXConnectorServer
344: try {
345: connectorServer.start();
346: connectorServerURLs[i] = connectorServer
347: .getAddress();
348: } catch (IllegalArgumentException e) {
349: throw e;
350: }
351: }
352: } catch (javax.naming.NameAlreadyBoundException ne) {
353: logger.log(BasicLevel.DEBUG, "Cannot start JMX service "
354: + ne);
355: throw new JonasAlreadyStartedException();
356: } catch (Exception e) {
357: logger
358: .log(BasicLevel.WARN, "Cannot start JMX service "
359: + e);
360: throw new ServiceException("Cannot start JMX service", e);
361: }
362:
363: // set back default.
364: ConfigurationRepository
365: .setCurrentConfiguration(ConfigurationRepository
366: .getDefaultConfiguration());
367:
368: }
369:
370: /**
371: * Stop this service
372: */
373: public void doStop() {
374: try {
375: InitialContext ictx = new InitialContext();
376: ictx.unbind("RMIConnector_" + getJonasServerName());
377: ictx.close();
378:
379: // Desactivates the connector server, that is, stops listening for
380: // client connections.
381: // Calling this method will also close all client connections that
382: // were made by this server.
383: // Being a potentialy slow operation, we keep it commented for the
384: // moment
385: for (int i = 0; i < connectorServers.length; i++) {
386: connectorServers[i].stop();
387: }
388: } catch (Exception e) {
389: logger.log(BasicLevel.ERROR,
390: "Cannot Unbind Jmx RMI Connector" + e);
391: }
392: // Unregister some MBeans
393: ObjectName domainOn = J2eeObjectName
394: .J2EEDomain(getDomainName());
395: try {
396: getJmxServer().unregisterMBean(domainOn);
397: } catch (Exception e) {
398: logger.log(BasicLevel.INFO,
399: "Cannot unregister JEEDomain MBean:"
400: + domainOn.toString());
401: }
402: ObjectName serverOn = J2eeObjectName.J2EEServer(
403: getDomainName(), getJonasServerName());
404: try {
405: getJmxServer().unregisterMBean(serverOn);
406: } catch (Exception e) {
407: logger.log(BasicLevel.INFO,
408: "Cannot unregister JEEServer MBean:"
409: + serverOn.toString());
410: }
411: // Remove internal references to the MBeanServer.
412: releaseJmxServer();
413:
414: if (logger.isLoggable(BasicLevel.DEBUG)) {
415: logger.log(BasicLevel.DEBUG, "JMX Service stopped");
416: }
417: }
418:
419: /**
420: * @return The actual adresses on which listen the created connector servers
421: */
422: public JMXServiceURL[] getConnectorServerURLs() {
423: return this .connectorServerURLs;
424: }
425:
426: /**
427: * Extract the protocol from a JMX connector URL
428: * @param url JMX connector url to parse
429: * @return protocol associated with the url
430: */
431: public static String getProtocolFromJmxConnectorUrl(String url) {
432:
433: int proIndMin = url.indexOf("/jndi/") + "/jndi/".length();
434: int proIndMax = url.indexOf("://");
435: String protocol = url.substring(proIndMin, proIndMax);
436:
437: // for the RMI protocol, we have to parse the connector name to identify
438: // the underlaying
439: // protocol : either jrmp or irmi
440: if (protocol.equals("rmi")) {
441: String subUrl = url.substring(proIndMax + "://".length());
442: int subProIndMin = subUrl.indexOf("/") + "/".length();
443: int subProIndMax = subUrl.indexOf("connector_");
444: protocol = subUrl.substring(subProIndMin, subProIndMax);
445: }
446:
447: return protocol;
448: }
449:
450: /**
451: * Get the provider url from a JMX connector URL
452: * @param url JMX connector url to parse
453: * @return provider url associated with the url
454: */
455: public static String getProviderUrlFromJmxConnectorUrl(String url) {
456:
457: int proIndMin = url.indexOf("/jndi/") + "/jndi/".length();
458: String protocol = getProtocolFromJmxConnectorUrl(url);
459: int urlIndMax = url.indexOf("/" + protocol + "connector");
460: return url.substring(proIndMin, urlIndMax);
461: }
462:
463: /**
464: * Get the MBeanServerConnection for the named server.
465: * Kept for backward compatibility (package org.allesta.wsabi.j2ee.provider.jonas)
466: * @deprecated Replaced by J2EEDomain.getConnection()
467: * @param serverName
468: * @return MBeanServerConnection
469: */
470: public MBeanServerConnection getServerConnection(String servername) {
471: return J2EEDomain.getInstance().getConnection(servername);
472: }
473: }
|