001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999-2004 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: */package org.objectweb.jonas.management;
022:
023: // general Java imports
024: import java.util.Hashtable;
025: import java.util.Properties;
026:
027: import javax.management.JMException;
028: import javax.management.MBeanServer;
029: import javax.management.MBeanServerNotification;
030: import javax.management.Notification;
031: import javax.management.NotificationListener;
032: import javax.management.ObjectName;
033:
034: import org.objectweb.jonas.common.JProp;
035: import org.objectweb.jonas.common.Log;
036: import org.objectweb.jonas.jmx.JonasObjectName;
037: import org.objectweb.util.monolog.api.BasicLevel;
038: import org.objectweb.util.monolog.api.Logger;
039:
040: /**
041: * This MBean allows persistent reconfiguration of a JOnAS server, alltogether with its embedded services,
042: * and possibly used resources like Data Sources ans Mail Factories.
043: * This class implements NotificationListener interface. The ReconfigManager adds itself as Listener to
044: * the following JMX Notification types (JAVA types):
045: * <ul>
046: * <li>MBeanServerNotification</li>, sent by the MBean server on MBeans registration/un-registration
047: * <li>Notification with type equal to ReconfigDispatcher.RECONFIG_TYPE</li>
048: * <li>Notification with type equal to ReconfigDispatcher.SAVE_RECONFIG_TYPE</li>
049: * </ul>
050: * @author Adriana Danes
051: * 04/09/20 Update with JSR77 JDBCDataSource MBeans
052: */
053: public class ReconfigManager implements ReconfigManagerMBean,
054: NotificationListener {
055:
056: /**
057: * The logger used in JOnAS
058: */
059: private static Logger logger = null;
060:
061: /**
062: * The MBean server attached to the current JOnAS server
063: */
064: MBeanServer jmxServer = null;
065:
066: // The following Strings reflect the current implementation of the org.objectweb.jonas.jmx.JonasObjectName class !!
067: // ------------------------------------------------------------------------------------------------------------------
068: // The value of the 'type' property in JonasObjectName of the services MBeans
069: private static final String SERVICE_TYPE = "service";
070:
071: // The value of the 'type' property in JonasObjectName used by DataSources
072: private static final String DATASOURCE_TYPE = "datasource";
073:
074: // J2EE Management specification conformance
075: // -----------------------------------------
076: /**
077: * The value of the j2eeType key for Mail Resources
078: */
079: private static final String MAIL_RESOURCE_TYPE = "JavaMailResource";
080: /**
081: * The value of the j2eeType key for JTA Resources
082: */
083: private static final String JTA_RESOURCE_TYPE = "JTAResource";
084: /**
085: * The value of the j2eeType key for JDBCDataSources
086: */
087: private static final String JDBC_RESOURCE_TYPE = "JDBCDataSource";
088:
089: /**
090: * Type of the security realm
091: */
092: private static final String SECURITYREALM_FACTORY = "securityfactory";
093:
094: /**
095: * Name of the security realm file
096: */
097: private static final String SECURITYREALM_FILE = "jonas-realm.xml";
098:
099: // My JMX name
100: ObjectName reconfigManagerObectName = JonasObjectName
101: .serverConfig();
102:
103: // The JOnAS server's initialization properties (saves several method call JProp.getInstance().getConfigFileEnv();)
104: // and configuration file name
105: Properties serverProperties = null;
106: String serverConfigFileName = null;
107:
108: // Table of Reconfigurator objects.
109: // We have a Reconfigurator object per each reconfigurable service + one for the server itself + one per resource
110: Hashtable reconfigurators = new Hashtable();
111:
112: /**
113: * Create the ReconfigManager MBean instance and add itself as listner to MBeanServerNotifications.
114: * @param serverProperties The JOnAS server's initial configuration properties
115: * @param jmxServer The JOnAS server's MBean server
116: */
117: public ReconfigManager(Properties serverProperties,
118: MBeanServer jmxServer) throws ReconfigException {
119: // get a logger for server traces
120: logger = Log.getLogger(Log.JONAS_MANAGEMENT_PREFIX);
121:
122: this .serverProperties = serverProperties;
123: this .jmxServer = jmxServer;
124: try {
125: this .serverConfigFileName = JProp.getInstance()
126: .getPropFile();
127: } catch (Exception e) {
128: throw new ReconfigException(
129: "Can't initialize ReconfigManager because of exception: "
130: + e.toString());
131: }
132:
133: // Add myself as listner to MBeanServerNotifications. Use for this the MBeanServerDelegate ObjectName as
134: // argument in the addNotificationListener method (see JMX API for more info).
135: // Use null NotificationFilter and null handback object.
136: try {
137: ObjectName delegate = new ObjectName(
138: "JMImplementation:type=MBeanServerDelegate");
139: jmxServer.addNotificationListener(delegate, this , null,
140: null);
141: } catch (JMException me) {
142: // MalformedObjectNameException should not occur if the JMX implementation is correct
143: // InstanceNotFoundException should not occur as we use MBeanServerDelegate ObjectName
144: throw new ReconfigException(
145: "ReconfigManager can't listen to MBeanServerNotifications because of exception: "
146: + me.toString());
147: }
148: if (logger.isLoggable(BasicLevel.DEBUG)) {
149: logger
150: .log(
151: BasicLevel.DEBUG,
152: "ReconfigManager MBean registered itself as listner to MBeanServerNotifications");
153: }
154: }
155:
156: /**
157: * Treat the notifications emitted by those MBeans having the ReconfigManager added as listener.
158: * This method determines the type of the notification and calls the specific treatement.
159: * @param notification received notification
160: * @param handback received handback object
161: */
162: public void handleNotification(Notification notification,
163: java.lang.Object handback) {
164: String notificationType = notification.getType();
165: if (notification instanceof MBeanServerNotification) {
166: // This notification is sent by the jmx server. It may be a REGISTRATION_NOTIFICATION or an UNREGISTRATION_NOTIFICATION
167: if (notificationType
168: .equals(MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
169: try {
170: handleRegistrationNotification((MBeanServerNotification) notification);
171: } catch (ReconfigException re) {
172: logger
173: .log(BasicLevel.ERROR,
174: "ReconfigManager error when trying to handle REGISTRATION_NOTIFICATION");
175: }
176: } else if (notificationType
177: .equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
178: if (logger.isLoggable(BasicLevel.DEBUG)) {
179: logger
180: .log(
181: BasicLevel.DEBUG,
182: "Received UNREGISTRATION_NOTIFICATION for MBean "
183: + ((MBeanServerNotification) notification)
184: .getMBeanName()
185: .toString());
186: }
187: // TO DO ...
188: }
189: } else {
190: // The MBean sending this notification has an associated Reconfigurator object created by the handleRegistrationNotification() method.
191: // Get the Reconfigurator's name from the 'message' field
192: String name = notification.getMessage();
193: // get the sequence number
194: long sequence = notification.getSequenceNumber();
195: if (notificationType
196: .equals(ReconfigDispatcher.RECONFIG_TYPE)) {
197: // get the reconfigured property (or properties) sent as a UserData within the notification
198: Reconfigured reconfigured = (Reconfigured) notification
199: .getUserData();
200: if (reconfigured instanceof ReconfiguredProp) {
201: ReconfiguredProp prop = (ReconfiguredProp) reconfigured;
202: handleReconfig(name, sequence, prop);
203: } else {
204: ReconfiguredXml reconfiguredXml = (ReconfiguredXml) reconfigured;
205: handleReconfig(name, sequence, reconfiguredXml);
206: }
207: } else if (notificationType
208: .equals(ReconfigDispatcher.SAVE_RECONFIG_TYPE)) {
209: handleSave(name, sequence);
210: }
211: }
212: }
213:
214: /**
215: * Treat REGISTRATION_NOTIFICATION methods. If the notification sender is a MBean associated to a service or resources,
216: * create a Reconfigurator associated to the sender and adds itself as listener
217: * for notifications emitted by the sender.
218: * @param notification notification to treat
219: * @throws ReconfigException pb. when trying to create reconfiguration support
220: */
221: private void handleRegistrationNotification(
222: MBeanServerNotification notification)
223: throws ReconfigException {
224: ObjectName notificationSender = notification.getMBeanName();
225: String senderType = notificationSender.getKeyProperty("type");
226: String senderName = notificationSender.getKeyProperty("name");
227: String senderJ2eeType = notificationSender
228: .getKeyProperty("j2eeType");
229: Reconfigurator reconfig = null;
230: if (senderJ2eeType != null) {
231: senderType = senderJ2eeType;
232: }
233:
234: if (senderType == null) {
235: // Nothing to do
236: return;
237: }
238:
239: if (senderType.equals(SERVICE_TYPE)) {
240: // The registered MBean is a JOnAS service MBean.
241: // Create a Reconfigurator for the service (the reconfigurator name is the service name)
242: // Currently only support Reconfiguration for JOnAS services
243: // (are not supported : the Log system and the server itself)
244: if (!senderName.equals("jonasServer")) {
245: if (senderName.equals("log")) {
246: // treat log system case
247: String configFileName = notificationSender
248: .getKeyProperty("fname");
249: try {
250: reconfig = new ReconfiguratorProp(senderName,
251: JProp.getInstance(configFileName)
252: .getPropFile(), JProp
253: .getInstance(configFileName)
254: .getConfigFileEnv());
255: } catch (Exception e) {
256: // This is a FileNotFound exception thrown by JProp.getInstance(resourceName)
257: // which means that the resource was not created from a configuration file at the server start-up.
258: // The persistent configuration of dynamically loaded resource is not implementes yet !!!!!
259: logger
260: .log(BasicLevel.WARN,
261: "Cannot do persistent reconfiguration for dynamically loaded resources!");
262: }
263: } else {
264: // treat regular jonas services
265: reconfig = new ReconfiguratorProp(senderName,
266: serverConfigFileName, serverProperties);
267: }
268: }
269: } else {
270: // Check if the registered MBean is a JOnAS resource
271: // The currently considered resources are Mail Factories and Datasources
272: String resourceName = null;
273: try {
274: if (senderType.equals(MAIL_RESOURCE_TYPE)) {
275: // get the name of the Mail Factory
276: resourceName = (String) jmxServer.getAttribute(
277: notificationSender, "FactoryName");
278: } else if (senderType.equals(JDBC_RESOURCE_TYPE)) {
279: // get the name of the Datasource
280: resourceName = (String) jmxServer.getAttribute(
281: notificationSender, "name");
282: } else if (senderType.equals(JTA_RESOURCE_TYPE)) {
283: // treat as the regular service case
284: reconfig = new ReconfiguratorProp(senderName,
285: serverConfigFileName, serverProperties);
286: // resource name keeps null so avoids entering in code (if) below
287: }
288:
289: // xml type
290: if (senderType.equals(SECURITYREALM_FACTORY)) {
291: JProp jprop = JProp.getInstance(SECURITYREALM_FILE);
292: String propsFilename = jprop.getPropFile();
293: String txt = jprop.getConfigFileXml();
294: // Create a Reconfigurator for this resource
295: reconfig = new ReconfiguratorXml(
296: SECURITYREALM_FILE, propsFilename, txt);
297: } else if (resourceName != null) {
298: JProp jprop = JProp.getInstance(resourceName);
299: String propsFilename = jprop.getPropFile();
300: Properties props = JProp.getInstance(resourceName)
301: .getConfigFileEnv();
302: // Create a Reconfigurator for this resource
303: reconfig = new ReconfiguratorProp(resourceName,
304: propsFilename, props);
305: }
306: } catch (JMException me) {
307: logger
308: .log(
309: BasicLevel.ERROR,
310: "Catched Exception when trying to treat reconfiguration of the following resource: "
311: + me);
312: throw new ReconfigException(
313: "Catched Exception when trying to treat reconfiguration of the following resource: "
314: + me.toString());
315: } catch (Exception e) {
316: // this exception is thrown by JProp.getInstance(resourceName)
317: // its not possible to receive it here because if the MBean registered itself, this call was already exceuted succesfully
318: logger.log(BasicLevel.ERROR,
319: "Catched Exception when calling JProp.getInstance("
320: + resourceName + "): " + e);
321: throw new ReconfigException(
322: "Catched Exception when calling JProp.getInstance("
323: + resourceName + "): " + e.toString());
324: }
325: }
326: if (reconfig != null) {
327: try {
328: jmxServer.addNotificationListener(notificationSender,
329: reconfigManagerObectName, null, null);
330: } catch (JMException me) {
331: logger.log(BasicLevel.ERROR,
332: "ReconfigManager can't listen to Notifications because of exception: "
333: + me.toString());
334: throw new ReconfigException(
335: "ReconfigManager can't listen to Notifications because of exception: "
336: + me.toString());
337: }
338: reconfigurators.put(senderName, reconfig);
339: if (logger.isLoggable(BasicLevel.DEBUG)) {
340: logger.log(BasicLevel.DEBUG,
341: "Received Registration Notification from "
342: + notificationSender.toString());
343: }
344: }
345: }
346:
347: /**
348: * Treat reconfiguration operation by calling the specific method on the associated reconfigurator
349: * @param name reconfigurator name
350: * @param sequence sequence number of the reconfiguration notification to treat
351: * @param prop reconfigured property (or properties)
352: */
353: private void handleReconfig(String name, long sequence,
354: ReconfiguredProp prop) throws ReconfigException {
355: if (logger.isLoggable(BasicLevel.DEBUG)) {
356: logger
357: .log(
358: BasicLevel.DEBUG,
359: "Received 'jonas.management.reconfiguration' notification concerning service or ressource: "
360: + name);
361: }
362: Reconfigurator reconfigurator = (ReconfiguratorProp) reconfigurators
363: .get(name);
364: if (reconfigurator == null) {
365: throw new ReconfigException(
366: "Can't find Reconfigurator associated to service or resource "
367: + name);
368: } else {
369: if (prop.getPropValue() != null) {
370: if (logger.isLoggable(BasicLevel.DEBUG)) {
371: logger.log(BasicLevel.DEBUG,
372: "Try to reconfigure property : "
373: + prop.getPropName()
374: + " using value value : "
375: + prop.getPropValue());
376: }
377: if (prop.replaceProp()) {
378: ((ReconfiguratorProp) reconfigurator).updateConfig(
379: prop.getPropName(), prop.getPropValue(),
380: sequence);
381: } else {
382: if (logger.isLoggable(BasicLevel.DEBUG)) {
383: if (prop.addProp()) {
384: logger
385: .log(BasicLevel.DEBUG,
386: "This value has to be added to the values sequence");
387: } else {
388: logger
389: .log(BasicLevel.DEBUG,
390: "This value has to be removed from the values sequence");
391: }
392: }
393: ((ReconfiguratorProp) reconfigurator).updateConfig(
394: prop.getPropName(), prop.getPropValue(),
395: prop.addProp(), sequence);
396: }
397: } else {
398: if (logger.isLoggable(BasicLevel.DEBUG)) {
399: logger
400: .log(BasicLevel.DEBUG,
401: "Reconfiguration made on a group of properties");
402: }
403: ((ReconfiguratorProp) reconfigurator).updateConfig(prop
404: .getProps(), sequence);
405: }
406: }
407: }
408:
409: /**
410: * Treat reconfiguration operation by calling the specific method on the associated reconfigurator
411: * @param name reconfigurator name
412: * @param sequence sequence number of the reconfiguration notification to treat
413: * @param reconfiguredXml reconfigured xml
414: */
415: private void handleReconfig(String name, long sequence,
416: ReconfiguredXml reconfiguredXml) throws ReconfigException {
417: if (logger.isLoggable(BasicLevel.DEBUG)) {
418: logger
419: .log(
420: BasicLevel.DEBUG,
421: "Received 'jonas.management.reconfiguration' notification concerning service or ressource: "
422: + name);
423: }
424: Reconfigurator reconfigurator = (ReconfiguratorXml) reconfigurators
425: .get(name);
426: if (reconfigurator == null) {
427: throw new ReconfigException(
428: "Can't find Reconfigurator associated to service or resource "
429: + name);
430: } else {
431: ((ReconfiguratorXml) reconfigurator).updateConfig(
432: reconfiguredXml.getXml(), sequence);
433: }
434: }
435:
436: /**
437: * Treat save operation by calling the specific method on the associated reconfigurator
438: * @param name reconfigurator name
439: * @param sequence sequence number of the reconfiguration notification to treat
440: */
441: private void handleSave(String name, long sequence)
442: throws ReconfigException {
443: if (logger.isLoggable(BasicLevel.DEBUG)) {
444: logger
445: .log(
446: BasicLevel.DEBUG,
447: "Received 'jonas.management.reconfiguration.save' notification concerning service or ressource: "
448: + name);
449: }
450: Reconfigurator reconfigurator = (Reconfigurator) reconfigurators
451: .get(name);
452: if (reconfigurator == null) {
453: throw new ReconfigException(
454: "Can't find Reconfigurator associated to service or resource "
455: + name);
456: } else {
457: reconfigurator.saveConfig(sequence);
458: }
459: }
460: }
|