001: package org.enhydra.util;
002:
003: import java.util.Enumeration;
004: import java.util.Hashtable;
005: import java.util.Iterator;
006: import java.util.StringTokenizer;
007: import java.util.Vector;
008:
009: import javax.management.Attribute;
010: import javax.management.AttributeChangeNotification;
011: import javax.management.AttributeList;
012: import javax.management.AttributeNotFoundException;
013: import javax.management.DynamicMBean;
014: import javax.management.InvalidAttributeValueException;
015: import javax.management.MBeanAttributeInfo;
016: import javax.management.MBeanConstructorInfo;
017: import javax.management.MBeanException;
018: import javax.management.MBeanInfo;
019: import javax.management.MBeanNotificationInfo;
020: import javax.management.MBeanOperationInfo;
021: import javax.management.Notification;
022: import javax.management.NotificationBroadcasterSupport;
023: import javax.management.ObjectName;
024: import javax.management.ReflectionException;
025: import javax.management.RuntimeOperationsException;
026:
027: import com.lutris.logging.LogChannel;
028: import com.lutris.logging.Logger;
029: import com.lutris.util.Config;
030: import com.lutris.util.KeywordValueException;
031:
032: /**
033: * Core MBean Implementation
034: *
035: * @author Slobodan Vujasinovic
036: * @author Tanja Jovanovic
037: */
038: public abstract class AbsConfigMBean extends
039: NotificationBroadcasterSupport implements DynamicMBean {
040:
041: protected String dClassName = this .getClass().getName();
042: protected MBeanInfo dMBeanInfo = null;
043:
044: protected ObjectName objectName = null;
045: protected Hashtable hashAttrib = new Hashtable();
046: protected Hashtable initHashAttrib = new Hashtable();
047:
048: protected Config config = null;
049: protected String prefix = null;
050:
051: protected Vector addedAttributes = null;
052:
053: public static String DOT = "_";
054:
055: protected long sequence = 0;
056: protected String noteTypesChange[] = { "jmx.attribute.change" };
057: protected String noteTypesSave[] = { "jmx.attribute.save" };
058: protected String noteTypesReset[] = { "jmx.attribute.reset" };
059: protected String noteTypesAdd[] = { "jmx.attribute.add" };
060: protected String noteTypesRemove[] = { "jmx.attribute.remove" };
061:
062: protected LogChannel logChannel = null;
063:
064: /**
065: * A constructor with no arguments is required.
066: */
067: public AbsConfigMBean() {
068: }
069:
070: protected String toValidIdentifier(String value) {
071: return value.replaceAll(".", DOT);
072: }
073:
074: protected String toOriginal(String value) {
075: return value.replaceAll(DOT, ".");
076: }
077:
078: public void initConfig(ObjectName objectName, String prefix,
079: String[] includes, String[] excludes) {
080: this .objectName = objectName;
081: this .prefix = prefix;
082: if ("".equals(prefix)) {
083: prefix = null;
084: }
085: addedAttributes = new Vector();
086: Hashtable originalHashtable = null;
087:
088: try {
089: originalHashtable = config.allConfigParams(null);
090: } catch (KeywordValueException kwe) {
091: originalHashtable = new Hashtable();
092: logChannel.write(Logger.DEBUG, kwe.toString());
093: }
094:
095: Hashtable includesHashtable = null;
096: Hashtable excludesHashtable = null;
097:
098: if (includes != null) {
099: includesHashtable = new Hashtable();
100: int length = originalHashtable.size();
101: Enumeration keys = originalHashtable.keys();
102: for (int i = length - 1; i >= 0; i--) {
103: String keyName = (String) keys.nextElement();
104: boolean include = false;
105: for (int j = 0; j < includes.length; j++) {
106: if (keyName.startsWith(includes[j])) {
107: include = true;
108: }
109: }
110: if (include) {
111: includesHashtable.put(keyName,
112: (String) originalHashtable.get(keyName));
113: }
114: }
115: } else {
116: includesHashtable = originalHashtable;
117: }
118:
119: if (excludes != null) {
120: excludesHashtable = new Hashtable();
121: int length = includesHashtable.size();
122: Enumeration keys = includesHashtable.keys();
123: for (int i = 0; i < length; i++) {
124: String keyName = (String) keys.nextElement();
125: boolean exclude = false;
126: for (int j = 0; j < excludes.length; j++) {
127: if (keyName.startsWith(excludes[j])) {
128: exclude = true;
129: }
130: }
131: if (exclude) {
132: excludesHashtable.put(keyName,
133: (String) includesHashtable.get(keyName));
134: }
135: }
136: } else {
137: excludesHashtable = new Hashtable();
138: }
139:
140: int length = includesHashtable.size();
141: Enumeration keys = includesHashtable.keys();
142: for (int i = 0; i < length; i++) {
143: String keyName = (String) keys.nextElement();
144:
145: if (!excludesHashtable.containsKey(keyName)) {
146: String keyValue = (String) includesHashtable
147: .get(keyName);
148:
149: if (prefix != null) {
150: keyName = keyName.substring(prefix.length() + 1);
151: }
152: hashAttrib.put(keyName, keyValue);
153: initHashAttrib.put(keyName, keyValue);
154: }
155: }
156: //buildDynamicMBeanInfo();
157: }
158:
159: public void buildDynamicMBeanInfo() {
160: // MBeanInfo initialization
161: dMBeanInfo = new MBeanInfo(dClassName, getDescription(),
162: getMBeanAttributesInfo(), getMBeanConstructorInfo(),
163: getMBeanOperationsInfo(), getMBeanNotificationInfo());
164: }
165:
166: /**
167: * -----------------------------------------------------
168: * ADDITIONALL PROTECTED METHODS
169: * -----------------------------------------------------
170: */
171:
172: /**
173: * Method returns MBean's MBeanAttributeInfo Array
174: * @return MBeanAttributeInfo[]
175: */
176: protected MBeanAttributeInfo[] getMBeanAttributesInfo() {
177: int length = hashAttrib.size();
178: MBeanAttributeInfo[] dAttributes = new MBeanAttributeInfo[length];
179: Enumeration keys = hashAttrib.keys();
180: for (int i = 0; i < length; i++) {
181: String attName = (String) keys.nextElement();
182: String attDesc = attName + " configuration parameter!";
183: dAttributes[i] = new MBeanAttributeInfo(attName,
184: "java.lang.String", attDesc, true, true, false);
185: }
186: return dAttributes;
187: }
188:
189: /**
190: * Method returns MBean's MBeanOperationInfo Array
191: * @return MBeanOperationInfo[]
192: */
193: protected abstract MBeanOperationInfo[] getMBeanOperationsInfo();
194:
195: /**
196: * Method returns MBean's MBeanNotificationInfo Array
197: * @return MBeanNotificationInfo[]
198: */
199: protected MBeanNotificationInfo[] getMBeanNotificationInfo() {
200: MBeanNotificationInfo[] dNotifications = new MBeanNotificationInfo[5];
201: dNotifications[0] = new MBeanNotificationInfo(noteTypesChange,
202: "javax.management.AttributeChangeNotification",
203: "Notifies listener of the attribute change");
204: dNotifications[1] = new MBeanNotificationInfo(noteTypesSave,
205: "javax.management.Notification",
206: "Notifies listnere that attributes have been saved to the config file");
207: dNotifications[2] = new MBeanNotificationInfo(noteTypesReset,
208: "javax.management.Notification",
209: "Notifies listnere that attributes have been reset to the initial value");
210: dNotifications[3] = new MBeanNotificationInfo(noteTypesAdd,
211: "javax.management.Notification",
212: "Notifies listener of the attribute change");
213: dNotifications[4] = new MBeanNotificationInfo(noteTypesRemove,
214: "javax.management.Notification",
215: "Notifies listener of the attribute change");
216:
217: return dNotifications;
218: }
219:
220: /**
221: * Method returns MBean's MBeanConstructorInfo Array
222: * @return MBeanConstructorInfo[]
223: */
224: protected abstract MBeanConstructorInfo[] getMBeanConstructorInfo();
225:
226: /**
227: * Method returns MBean's description
228: * @return String
229: */
230: protected String getDescription() {
231: String dDescription = "Dynamic MBean manages application configuration";
232: return dDescription;
233: }
234:
235: /*
236: * -----------------------------------------------------
237: * IMPLEMENTATION OF THE DynamicMBean INTERFACE
238: * -----------------------------------------------------
239: */
240:
241: /**
242: * Allows the value of the specified attribute of the Dynamic MBean to be obtained.
243: */
244: public Object getAttribute(String attribute_name)
245: throws AttributeNotFoundException, MBeanException,
246: ReflectionException {
247: // Check attribute_name is not null to avoid NullPointerException later on
248: if (attribute_name == null) {
249: throw new RuntimeOperationsException(
250: new IllegalArgumentException(
251: "Attribute name cannot be null"),
252: "Cannot invoke a getter of " + dClassName
253: + " with null attribute name");
254: }
255: // Check for a recognized attribute_name and call the corresponding getter
256: try {
257: return hashAttrib.get(attribute_name);
258: } catch (Exception e) {
259: // If attribute_name has not been recognized throw an AttributeNotFoundException
260: throw (new AttributeNotFoundException("Cannot find "
261: + attribute_name + " attribute in " + dClassName));
262: }
263: }
264:
265: /**
266: * Sets the value of the specified attribute of the Dynamic MBean.
267: */
268: public void setAttribute(Attribute attribute)
269: throws AttributeNotFoundException,
270: InvalidAttributeValueException, MBeanException,
271: ReflectionException {
272:
273: // Check attribute is not null to avoid NullPointerException later on
274: if (attribute == null) {
275: throw new RuntimeOperationsException(
276: new IllegalArgumentException(
277: "Attribute cannot be null"),
278: "Cannot invoke a setter of " + dClassName
279: + " with null attribute");
280: }
281:
282: String name = attribute.getName();
283: String value = (String) attribute.getValue();
284:
285: try {
286: /**
287: boolean newOne = !hashAttrib.containsKey(name);
288: if (prefix==null){
289: newOne = !config.containsKey(name);
290: } else {
291: newOne = !config.containsKey(prefix+DOT+name);
292: }
293: */
294:
295: String oldValue = (String) hashAttrib.get(name);
296:
297: hashAttrib.put(name, value);
298:
299: String[] stringArray = null;
300: if (name.endsWith("_Array")) {
301: int len = name.length();
302: name = name.substring(0, len - 6);
303: StringTokenizer tok = new StringTokenizer(value, ",");
304: stringArray = new String[tok.countTokens()];
305: int i = 0;
306: while (tok.hasMoreTokens()) {
307: stringArray[i] = tok.nextToken().trim();
308: i++;
309: }
310:
311: try {
312: if (prefix == null) {
313: config.set(toOriginal(name), stringArray);
314: config.getConfigFile().addEntry(
315: toOriginal(name), stringArray, "");
316: } else {
317: config.set(toOriginal(prefix + DOT + name),
318: stringArray);
319: config.getConfigFile().addEntry(
320: toOriginal(prefix + DOT + name),
321: stringArray, "");
322: }
323: } catch (Exception e) {
324: logChannel.write(Logger.DEBUG, e.toString());
325: }
326: } else {
327: try {
328: if (prefix == null) {
329: config.set(toOriginal(name), value);
330: config.getConfigFile().addEntry(
331: toOriginal(name), value, "");
332: } else {
333: config.set(toOriginal(prefix + DOT + name),
334: value);
335: config.getConfigFile().addEntry(
336: toOriginal(prefix + DOT + name), value,
337: "");
338: }
339: } catch (Exception e) {
340: logChannel.write(Logger.DEBUG, e.toString());
341: }
342: }
343:
344: String notificationString = "Attribute " + name
345: + " has been set from " + oldValue + " to " + value;
346: Notification note = new AttributeChangeNotification(
347: objectName, ++sequence, System.currentTimeMillis(),
348: notificationString, name, "String", oldValue, value);
349: sendNotification(note);
350: } catch (Exception e) {
351: throw (new AttributeNotFoundException("Cannot find " + name
352: + " attribute in " + dClassName));
353: }
354: }
355:
356: /**
357: * Sets the values of several attributes of the Dynamic MBean, and returns the
358: * list of attributes that have been set.
359: */
360: public AttributeList setAttributes(AttributeList attributes) {
361: // Check attributes is not null to avoid NullPointerException later on
362: if (attributes == null) {
363: throw new RuntimeOperationsException(
364: new IllegalArgumentException(
365: "AttributeList attributes cannot be null"),
366: "Cannot invoke a setter of " + dClassName);
367: }
368: AttributeList resultList = new AttributeList();
369:
370: // if attributeNames is empty, nothing more to do
371: if (attributes.isEmpty()) {
372: return resultList;
373: }
374:
375: // for each attribute, try to set it and add to the result list if successfull
376: for (Iterator i = attributes.iterator(); i.hasNext();) {
377: Attribute attr = (Attribute) i.next();
378: try {
379: setAttribute(attr);
380: String name = attr.getName();
381: Object value = getAttribute(name);
382: resultList.add(new Attribute(name, value));
383: } catch (Exception e) {
384: logChannel.write(Logger.DEBUG, e.toString());
385: }
386: }
387: return resultList;
388: }
389:
390: /**
391: * get the values of several attributes of the Dynamic MBean.
392: */
393: public AttributeList getAttributes(String[] attributeNames) {
394:
395: // Check attributeNames is not null to avoid NullPointerException later on
396: if (attributeNames == null) {
397: throw new RuntimeOperationsException(
398: new IllegalArgumentException(
399: "attributeNames[] cannot be null"),
400: "Cannot invoke a getter of " + dClassName);
401: }
402:
403: AttributeList resultList = new AttributeList();
404:
405: // if attributeNames is empty, return an empty result list
406: if (attributeNames.length == 0) {
407: return resultList;
408: }
409:
410: // build the result attribute list
411: for (int i = 0; i < attributeNames.length; i++) {
412: try {
413: Object value = getAttribute((String) attributeNames[i]);
414: resultList.add(new Attribute(attributeNames[i], value));
415: } catch (Exception e) {
416: logChannel.write(Logger.DEBUG, e.toString());
417: }
418: }
419: return resultList;
420: }
421:
422: /**
423: * This method provides the exposed attributes and operations of the Dynamic MBean.
424: * It provides this information using an MBeanInfo object.
425: */
426: public MBeanInfo getMBeanInfo() {
427: // return the information we want to expose for management:
428: // the dMBeanInfo private field has been built at instanciation time,
429: return dMBeanInfo;
430: }
431:
432: /**
433: * -----------------------------------------------------
434: * ADDITIONALL PUBLIC METHODS
435: * -----------------------------------------------------
436: */
437:
438: /**
439: * Save Config Attributes
440: */
441: public void saveAttributes() {
442: try {
443: ConfigFileInterface configFile = config.getConfigFile();
444:
445: configFile.write();
446:
447: Notification note = new Notification(noteTypesSave[0],
448: objectName, ++sequence, System.currentTimeMillis());
449: sendNotification(note);
450: } catch (Exception e) {
451: logChannel.write(Logger.DEBUG, e.toString());
452: }
453: }
454:
455: /**
456: * Add new attribute to config
457: */
458: public void addAttribute(String name, String value) {
459: try {
460: if (name != null && value != null) {
461:
462: name = name.trim();
463:
464: boolean newOne = true;
465: String tempName = name;
466: if (tempName.endsWith("_Array")) {
467: int len = tempName.length();
468: tempName = tempName.substring(0, len - 6);
469: }
470:
471: if (prefix == null
472: && config.containsKey(toOriginal(tempName))) {
473: newOne = false;
474: } else if (config.containsKey(toOriginal(prefix + DOT
475: + tempName))) {
476: newOne = false;
477: }
478:
479: Attribute att = new Attribute(name, (String) value);
480: setAttribute(att);
481:
482: if (newOne) {
483: addedAttributes.add(tempName);
484: }
485:
486: buildDynamicMBeanInfo();
487:
488: Notification note = new Notification(noteTypesAdd[0],
489: (NotificationBroadcasterSupport) this ,
490: ++sequence, System.currentTimeMillis());
491: sendNotification(note);
492: }
493: } catch (Exception e) {
494: logChannel.write(Logger.DEBUG, e.toString());
495: }
496: }
497:
498: /**
499: * Operation: reset attributes to their initial values
500: */
501: public void reset() throws AttributeNotFoundException {
502:
503: try {
504: int length = 0;
505: Enumeration keys = null;
506:
507: // hashAttrib table reinitialization
508: length = initHashAttrib.size();
509: keys = initHashAttrib.keys();
510: hashAttrib.clear();
511: for (int i = 0; i < length; i++) {
512: String keyName = (String) keys.nextElement();
513: String keyValue = (String) initHashAttrib.get(keyName);
514: hashAttrib.put(keyName, keyValue);
515: }
516:
517: length = hashAttrib.size();
518: keys = hashAttrib.keys();
519:
520: // adapt config attribute value (or add attribute if previously removed)
521: for (int i = 0; i < length; i++) {
522: String keyName = (String) keys.nextElement();
523: String keyValue = (String) hashAttrib.get(keyName);
524:
525: if (keyName.endsWith("_Array")) {
526: int len = keyName.length();
527: keyName = keyName.substring(0, len - 6);
528: StringTokenizer tok = new StringTokenizer(keyValue,
529: ",");
530: String[] stringArray = new String[tok.countTokens()];
531: int j = 0;
532: while (tok.hasMoreTokens()) {
533: stringArray[j] = tok.nextToken().trim();
534: j++;
535: }
536:
537: try {
538: if (prefix == null) {
539: config
540: .set(toOriginal(keyName),
541: stringArray);
542: config.getConfigFile().addEntry(
543: toOriginal(keyName), stringArray,
544: "");
545: } else {
546: config.set(toOriginal(prefix + DOT
547: + keyName), stringArray);
548: config.getConfigFile().addEntry(
549: toOriginal(prefix + DOT + keyName),
550: stringArray, "");
551: }
552: } catch (Exception e) {
553: logChannel.write(Logger.DEBUG, e.toString());
554: }
555: } else {
556: try {
557: if (prefix == null) {
558: config.set(toOriginal(keyName), keyValue);
559: config.getConfigFile().addEntry(
560: toOriginal(keyName), keyValue, "");
561: } else {
562: config.set(toOriginal(prefix + DOT
563: + keyName), keyValue);
564: config.getConfigFile().addEntry(
565: toOriginal(prefix + DOT + keyName),
566: keyValue, "");
567: }
568: } catch (Exception e) {
569: logChannel.write(Logger.DEBUG, e.toString());
570: }
571: }
572:
573: }
574:
575: // remove previously added atributes
576: length = addedAttributes.size();
577: for (int i = 0; i < length; i++) {
578: String keyName = (String) addedAttributes.elementAt(i);
579: if (!hashAttrib.contains(keyName)) {
580: if (prefix == null) {
581: config.remove(toOriginal(keyName));
582: config.getConfigFile().removeEntry(
583: toOriginal(keyName));
584: } else {
585: config
586: .remove(toOriginal(prefix + DOT
587: + keyName));
588: config.getConfigFile().removeEntry(
589: toOriginal(prefix + DOT + keyName));
590: }
591: }
592: }
593: addedAttributes.clear();
594: } catch (Exception e) {
595: logChannel.write(Logger.DEBUG, e.toString());
596: }
597:
598: buildDynamicMBeanInfo();
599: Notification note = new Notification(noteTypesReset[0],
600: objectName, ++sequence, System.currentTimeMillis());
601: sendNotification(note);
602: }
603:
604: /*
605: * -----------------------------------------------------
606: * Remove attribute from config
607: * -----------------------------------------------------
608: */
609: public void removeAttribute(String name) {
610: try {
611: if (name != null) {
612: name = name.trim();
613: if (hashAttrib.containsKey(name)) {
614: hashAttrib.remove(name);
615:
616: if (name.endsWith("_Array")) {
617: int len = name.length();
618: name = name.substring(0, len - 6);
619: }
620:
621: if (prefix == null) {
622: config.remove(toOriginal(name));
623: config.getConfigFile().removeEntry(
624: toOriginal(name));
625: } else {
626: config.remove(toOriginal(prefix + DOT + name));
627: config.getConfigFile().removeEntry(
628: toOriginal(prefix + DOT + name));
629: }
630:
631: buildDynamicMBeanInfo();
632:
633: Notification note = new Notification(
634: noteTypesAdd[0],
635: (NotificationBroadcasterSupport) this ,
636: ++sequence, System.currentTimeMillis());
637: sendNotification(note);
638: }
639: }
640: } catch (Exception e) {
641: logChannel.write(Logger.DEBUG, e.toString());
642: }
643: }
644:
645: /**
646: * Allows an operation to be invoked on the Dynamic MBean.
647: */
648: public abstract Object invoke(String operationName,
649: Object params[], String signature[]) throws MBeanException,
650: ReflectionException;
651:
652: }
|