001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package mx4j.tools.config;
010:
011: import java.io.BufferedInputStream;
012: import java.io.IOException;
013: import java.io.InputStream;
014: import java.io.InterruptedIOException;
015: import java.lang.reflect.Constructor;
016: import java.lang.reflect.InvocationTargetException;
017: import java.lang.reflect.Method;
018: import java.net.InetAddress;
019: import java.net.ServerSocket;
020: import java.net.Socket;
021: import java.util.ArrayList;
022: import java.util.HashMap;
023: import java.util.List;
024: import java.util.Map;
025:
026: import javax.management.Attribute;
027: import javax.management.MBeanServer;
028: import javax.management.MalformedObjectNameException;
029: import javax.management.ObjectName;
030:
031: import mx4j.log.Log;
032: import mx4j.log.Logger;
033: import org.w3c.dom.Element;
034: import org.w3c.dom.NamedNodeMap;
035:
036: /**
037: * @version $Revision: 1.8 $
038: */
039: public class DefaultConfigurationBuilder implements
040: ConfigurationBuilder {
041: public static final String SHUTDOWN_COMMAND = "shutdown";
042: public static final String RESTART_COMMAND = "restart";
043:
044: private static final String NULL = "null";
045:
046: public Node createConfigurationNode(Element node)
047: throws ConfigurationException {
048: String loweredName = node.getNodeName().toLowerCase();
049: StringBuffer buffer = new StringBuffer(loweredName);
050: buffer.replace(0, 1, loweredName.substring(0, 1).toUpperCase());
051: String className = getClass().getName() + "$"
052: + buffer.toString();
053: try {
054: Logger logger = getLogger();
055: if (logger.isEnabledFor(Logger.TRACE))
056: logger
057: .trace("Creating configuration node "
058: + className);
059: return (ConfigurationBuilder.Node) getClass()
060: .getClassLoader().loadClass(className)
061: .newInstance();
062: } catch (Exception x) {
063: throw new ConfigurationException(x);
064: }
065: }
066:
067: private static Logger getLogger() {
068: return Log.getLogger(DefaultConfigurationBuilder.class
069: .getName());
070: }
071:
072: public abstract static class AbstractNode implements Node {
073: private String text;
074: private Node parent;
075: private List children;
076:
077: public void setText(String text) {
078: this .text = text;
079: }
080:
081: public void setParent(Node parent) {
082: this .parent = parent;
083: }
084:
085: public void addChild(Node child) {
086: if (children == null)
087: children = new ArrayList();
088: child.setParent(this );
089: children.add(child);
090: }
091:
092: protected String getText() {
093: return text;
094: }
095:
096: public Node getParent() {
097: return parent;
098: }
099:
100: public List getChildren() {
101: return children;
102: }
103:
104: public void setAttributes(NamedNodeMap attributes)
105: throws ConfigurationException {
106: Logger logger = getLogger();
107: for (int i = 0; i < attributes.getLength(); ++i) {
108: org.w3c.dom.Node attribute = attributes.item(i);
109: String name = attribute.getNodeName();
110: String value = attribute.getNodeValue();
111: String setterName = "set"
112: + name.substring(0, 1).toUpperCase()
113: + name.substring(1);
114: try {
115: if (logger.isEnabledFor(Logger.TRACE))
116: logger.trace("Calling " + setterName + " with "
117: + value + " on " + this );
118: Method setter = getClass().getMethod(setterName,
119: new Class[] { String.class });
120: setter.invoke(this ,
121: new java.lang.Object[] { value });
122: } catch (InvocationTargetException x) {
123: throw new ConfigurationException(x
124: .getTargetException());
125: } catch (Exception x) {
126: throw new ConfigurationException(x);
127: }
128: }
129: }
130: }
131:
132: public static class Configuration extends AbstractNode implements
133: ObjectsHolder, Runnable {
134: private Map objects;
135: private int port = -1;
136: private MBeanServer server;
137: private Thread thread;
138:
139: public void setPort(String portString) {
140: this .port = Integer.parseInt(portString);
141: }
142:
143: public java.lang.Object configure(MBeanServer server)
144: throws ConfigurationException {
145: if (server != null) {
146: this .server = server;
147: return startup(server);
148: } else {
149: return shutdown(this .server);
150: }
151: }
152:
153: private java.lang.Object startup(MBeanServer server)
154: throws ConfigurationException {
155: Logger logger = getLogger();
156: List children = getChildren();
157: if (children != null) {
158: for (int i = 0; i < children.size(); ++i) {
159: Node child = (Node) children.get(i);
160: if (child instanceof DefaultConfigurationBuilder.Startup)
161: child.configure(server);
162: }
163: }
164: if (port > 0) {
165: thread = new Thread(this , "Configuration Shutdown");
166: if (logger.isEnabledFor(Logger.TRACE))
167: logger.trace("Starting " + thread.getName()
168: + " Thread on port " + port);
169: thread.start();
170: }
171: return null;
172: }
173:
174: private java.lang.Object shutdown(MBeanServer server)
175: throws ConfigurationException {
176: Logger logger = getLogger();
177: List children = getChildren();
178: if (children != null) {
179: for (int i = 0; i < children.size(); ++i) {
180: Node child = (Node) children.get(i);
181: if (child instanceof DefaultConfigurationBuilder.Shutdown)
182: child.configure(server);
183: }
184: }
185: if (port > 0) {
186: if (logger.isEnabledFor(Logger.TRACE))
187: logger.trace("Stopping " + thread.getName()
188: + " Thread on port " + port);
189: thread.interrupt();
190: }
191: return null;
192: }
193:
194: public void run() {
195: Logger logger = getLogger();
196: ServerSocket server = null;
197: try {
198: if (logger.isEnabledFor(Logger.TRACE))
199: logger.trace("Started " + thread.getName()
200: + " Thread on port " + port);
201:
202: server = new ServerSocket(port, 50, InetAddress
203: .getByName(null));
204: server.setSoTimeout(1000);
205:
206: byte[] buffer = new byte[64];
207: StringBuffer command = new StringBuffer();
208: while (!thread.isInterrupted()) {
209: Socket client = null;
210: try {
211: client = server.accept();
212: } catch (InterruptedIOException x) {
213: continue;
214: }
215: if (logger.isEnabledFor(Logger.TRACE))
216: logger.trace("Client connected " + client);
217: InputStream is = new BufferedInputStream(client
218: .getInputStream());
219:
220: command.setLength(0);
221: int read = -1;
222: while ((read = is.read(buffer)) >= 0)
223: command.append(new String(buffer, 0, read));
224:
225: String cmd = command.toString();
226: if (logger.isEnabledFor(Logger.TRACE))
227: logger.trace("Got command '" + cmd + "'");
228:
229: if (SHUTDOWN_COMMAND.equals(cmd)) {
230: try {
231: configure(null);
232: break;
233: } catch (ConfigurationException x) {
234: if (logger.isEnabledFor(Logger.WARN))
235: logger
236: .warn(
237: "Bad configuration for shutdown",
238: x);
239: }
240: }
241: }
242: } catch (Exception x) {
243: if (logger.isEnabledFor(Logger.INFO))
244: logger.info("Caught Exception in "
245: + thread.getName() + " Thread, exiting", x);
246: } finally {
247: if (logger.isEnabledFor(Logger.TRACE))
248: logger.trace("Stopped " + thread.getName()
249: + " Thread on port " + port);
250: try {
251: if (server != null)
252: server.close();
253: } catch (IOException x) {
254: }
255: }
256: }
257:
258: public java.lang.Object getObject(String key) {
259: if (objects == null)
260: return null;
261: return objects.get(key);
262: }
263:
264: public java.lang.Object putObject(String key,
265: java.lang.Object value) {
266: if (objects == null)
267: objects = new HashMap();
268: return objects.put(key, value);
269: }
270:
271: public boolean containsKey(String key) {
272: if (objects == null)
273: return false;
274: return objects.containsKey(key);
275: }
276: }
277:
278: public static class Startup extends AbstractNode {
279: public java.lang.Object configure(MBeanServer server)
280: throws ConfigurationException {
281: List children = getChildren();
282: if (children != null) {
283: for (int i = 0; i < children.size(); ++i) {
284: Node child = (Node) children.get(i);
285: child.configure(server);
286: }
287: }
288: return null;
289: }
290: }
291:
292: public static class Shutdown extends AbstractNode {
293: public java.lang.Object configure(MBeanServer server)
294: throws ConfigurationException {
295: List children = getChildren();
296: if (children != null) {
297: for (int i = 0; i < children.size(); ++i) {
298: Node child = (Node) children.get(i);
299: child.configure(server);
300: }
301: }
302: return null;
303: }
304: }
305:
306: public static class Object extends AbstractNode {
307: private String id;
308:
309: public void setObjectid(String id) {
310: this .id = id;
311: }
312:
313: public String getObjectid() {
314: return id;
315: }
316:
317: public java.lang.Object configure(MBeanServer server)
318: throws ConfigurationException {
319: List children = getChildren();
320: java.lang.Object result = null;
321: if (children != null && children.size() > 0) {
322: Node child = (Node) children.get(0);
323: result = child.configure(server);
324: }
325: putObject(this , id, result);
326: return result;
327: }
328: }
329:
330: public static class New extends AbstractNode {
331: private String classname;
332:
333: public void setClassname(String classname) {
334: this .classname = classname;
335: }
336:
337: public java.lang.Object configure(MBeanServer server)
338: throws ConfigurationException {
339: try {
340: Class cls = loadClass(classname);
341: Constructor ctor = cls
342: .getConstructor(getMethodSignature(this ));
343: return ctor
344: .newInstance(getMethodArguments(this , server));
345: } catch (InvocationTargetException x) {
346: throw new ConfigurationException(x.getTargetException());
347: } catch (ConfigurationException x) {
348: throw x;
349: } catch (Exception x) {
350: throw new ConfigurationException(x);
351: }
352: }
353: }
354:
355: public static class Arg extends AbstractNode {
356: private static final String OBJECT_TYPE = "object";
357: private static final String STRING_TYPE = "string";
358: private static final String BOOLEAN_TYPE = "boolean";
359: private static final String BYTE_TYPE = "byte";
360: private static final String CHAR_TYPE = "char";
361: private static final String DOUBLE_TYPE = "double";
362: private static final String FLOAT_TYPE = "float";
363: private static final String INT_TYPE = "int";
364: private static final String LONG_TYPE = "long";
365: private static final String SHORT_TYPE = "short";
366:
367: private String type;
368: private String refobjectid;
369:
370: public void setType(String type) {
371: this .type = type;
372: }
373:
374: public void setRefobjectid(String refobjectid) {
375: this .refobjectid = refobjectid;
376: }
377:
378: public Class getJavaType() throws ConfigurationException {
379: if (STRING_TYPE.equalsIgnoreCase(type))
380: return String.class;
381: if (OBJECT_TYPE.equalsIgnoreCase(type))
382: return java.lang.Object.class;
383: if (BOOLEAN_TYPE.equalsIgnoreCase(type))
384: return boolean.class;
385: if (BYTE_TYPE.equalsIgnoreCase(type))
386: return byte.class;
387: if (CHAR_TYPE.equalsIgnoreCase(type))
388: return char.class;
389: if (DOUBLE_TYPE.equalsIgnoreCase(type))
390: return double.class;
391: if (FLOAT_TYPE.equalsIgnoreCase(type))
392: return float.class;
393: if (INT_TYPE.equalsIgnoreCase(type))
394: return int.class;
395: if (LONG_TYPE.equalsIgnoreCase(type))
396: return long.class;
397: if (SHORT_TYPE.equalsIgnoreCase(type))
398: return short.class;
399: return loadClass(type);
400: }
401:
402: public java.lang.Object configure(MBeanServer server)
403: throws ConfigurationException {
404: if (refobjectid != null)
405: return getObject(this , refobjectid);
406:
407: List children = getChildren();
408: if (children != null && children.size() > 0) {
409: Node child = (Node) children.get(0);
410: return child.configure(server);
411: }
412:
413: String text = getText();
414: if (text == null || NULL.equals(text))
415: return null;
416:
417: if (STRING_TYPE.equalsIgnoreCase(type))
418: return text;
419: if (OBJECT_TYPE.equalsIgnoreCase(type))
420: return text;
421: if (BOOLEAN_TYPE.equalsIgnoreCase(type))
422: return Boolean.valueOf(text);
423: if (BYTE_TYPE.equalsIgnoreCase(type))
424: return Byte.valueOf(text);
425: if (CHAR_TYPE.equalsIgnoreCase(type))
426: return new Character(text.length() < 1 ? 0 : text
427: .charAt(0));
428: if (DOUBLE_TYPE.equalsIgnoreCase(type))
429: return Double.valueOf(text);
430: if (FLOAT_TYPE.equalsIgnoreCase(type))
431: return Float.valueOf(text);
432: if (INT_TYPE.equalsIgnoreCase(type))
433: return Integer.valueOf(text);
434: if (LONG_TYPE.equalsIgnoreCase(type))
435: return Long.valueOf(text);
436: if (SHORT_TYPE.equalsIgnoreCase(type))
437: return Short.valueOf(text);
438:
439: try {
440: Constructor ctor = getJavaType().getConstructor(
441: new Class[] { String.class });
442: return ctor
443: .newInstance(new java.lang.Object[] { text });
444: } catch (InvocationTargetException x) {
445: throw new ConfigurationException(x.getTargetException());
446: } catch (ConfigurationException x) {
447: throw x;
448: } catch (Exception x) {
449: throw new ConfigurationException(x);
450: }
451: }
452: }
453:
454: public static class Register extends AbstractNode {
455: private ObjectName objectname;
456:
457: public void setObjectname(String name)
458: throws MalformedObjectNameException {
459: if (name != null && !NULL.equals(name))
460: this .objectname = ObjectName.getInstance(name);
461: }
462:
463: public java.lang.Object configure(MBeanServer server)
464: throws ConfigurationException {
465: List children = getChildren();
466: if (children != null && children.size() > 0) {
467: Node child = (Node) children.get(0);
468: try {
469: return server.registerMBean(
470: child.configure(server), objectname);
471: } catch (ConfigurationException x) {
472: throw x;
473: } catch (Exception x) {
474: throw new ConfigurationException(x);
475: }
476: }
477: return null;
478: }
479: }
480:
481: public static class Unregister extends AbstractNode {
482: private ObjectName objectname;
483:
484: public void setObjectname(String name)
485: throws MalformedObjectNameException {
486: this .objectname = ObjectName.getInstance(name);
487: }
488:
489: public java.lang.Object configure(MBeanServer server)
490: throws ConfigurationException {
491: try {
492: server.unregisterMBean(objectname);
493: return null;
494: } catch (Exception x) {
495: throw new ConfigurationException(x);
496: }
497: }
498: }
499:
500: public static class Create extends AbstractNode {
501: private String classname;
502: private ObjectName objectname;
503: private String loadername;
504:
505: public void setClassname(String classname) {
506: this .classname = classname;
507: }
508:
509: public void setObjectname(String name)
510: throws MalformedObjectNameException {
511: if (name != null && !NULL.equals(name))
512: this .objectname = ObjectName.getInstance(name);
513: }
514:
515: public void setLoadername(String name)
516: throws MalformedObjectNameException {
517: this .loadername = name;
518: }
519:
520: public java.lang.Object configure(MBeanServer server)
521: throws ConfigurationException {
522: try {
523: if (loadername != null) {
524: ObjectName loader = null;
525: if (!NULL.equals(loadername))
526: loader = ObjectName.getInstance(loadername);
527: return server.createMBean(classname, objectname,
528: loader, getMethodArguments(this , server),
529: getJMXMethodSignature(this ));
530: } else {
531: return server.createMBean(classname, objectname,
532: getMethodArguments(this , server),
533: getJMXMethodSignature(this ));
534: }
535: } catch (ConfigurationException x) {
536: throw x;
537: } catch (Exception x) {
538: throw new ConfigurationException(x);
539: }
540: }
541: }
542:
543: public static class Call extends AbstractNode {
544: private String classname;
545: private ObjectName objectname;
546: private String refobjectid;
547: private String method;
548: private String operation;
549: private String attribute;
550:
551: public void setClassname(String classname) {
552: this .classname = classname;
553: }
554:
555: public void setObjectname(String name)
556: throws MalformedObjectNameException {
557: if (name != null && !NULL.equals(name))
558: this .objectname = ObjectName.getInstance(name);
559: }
560:
561: public void setRefobjectid(String refid) {
562: this .refobjectid = refid;
563: }
564:
565: public void setMethod(String method) {
566: this .method = method;
567: }
568:
569: public void setOperation(String operation) {
570: this .operation = operation;
571: }
572:
573: public void setAttribute(String attribute) {
574: this .attribute = attribute;
575: }
576:
577: public java.lang.Object configure(MBeanServer server)
578: throws ConfigurationException {
579: if (classname != null) {
580: // Static call
581: Class cls = loadClass(classname);
582: try {
583: Method mthd = cls.getMethod(method,
584: getMethodSignature(this ));
585: return mthd.invoke(null, getMethodArguments(this ,
586: server));
587: } catch (InvocationTargetException x) {
588: throw new ConfigurationException(x
589: .getTargetException());
590: } catch (ConfigurationException x) {
591: throw x;
592: } catch (Exception x) {
593: throw new ConfigurationException(x);
594: }
595: } else {
596: if (objectname != null) {
597: // JMX call
598: if (operation != null) {
599: try {
600: return server.invoke(objectname, operation,
601: getMethodArguments(this , server),
602: getJMXMethodSignature(this ));
603: } catch (ConfigurationException x) {
604: throw x;
605: } catch (Exception x) {
606: throw new ConfigurationException(x);
607: }
608: } else if (attribute != null) {
609: try {
610: List children = getChildren();
611: if (children == null || children.size() < 1) {
612: return server.getAttribute(objectname,
613: attribute);
614: } else {
615: java.lang.Object arg = getMethodArguments(
616: this , server)[0];
617: server.setAttribute(objectname,
618: new Attribute(attribute, arg));
619: return null;
620: }
621: } catch (ConfigurationException x) {
622: throw x;
623: } catch (Exception x) {
624: throw new ConfigurationException(x);
625: }
626: } else {
627: throw new ConfigurationException(
628: "Missing 'attribute' or 'operation' attribute in JMX call");
629: }
630: } else {
631: // Standard call
632: java.lang.Object target = null;
633: if (refobjectid != null) {
634: target = getObject(this , refobjectid);
635: if (target == null)
636: throw new ConfigurationException(
637: "Could not find object with id "
638: + refobjectid);
639: try {
640: Method mthd = target.getClass().getMethod(
641: method, getMethodSignature(this ));
642: return mthd.invoke(target,
643: getMethodArguments(this , server));
644: } catch (InvocationTargetException x) {
645: throw new ConfigurationException(x
646: .getTargetException());
647: } catch (ConfigurationException x) {
648: throw x;
649: } catch (Exception x) {
650: throw new ConfigurationException(x);
651: }
652: } else {
653: throw new ConfigurationException(
654: "Missing 'refobjectid' attribute in call element");
655: }
656: }
657: }
658: }
659: }
660:
661: private static Class[] getMethodSignature(Node node)
662: throws ConfigurationException {
663: List children = node.getChildren();
664: if (children == null)
665: return null;
666: ArrayList signature = new ArrayList();
667: for (int i = 0; i < children.size(); ++i) {
668: Node child = (Node) children.get(i);
669: if (child instanceof Arg) {
670: Arg arg = (Arg) child;
671: signature.add(arg.getJavaType());
672: }
673: }
674: return (Class[]) signature.toArray(new Class[signature.size()]);
675: }
676:
677: private static String[] getJMXMethodSignature(Node node)
678: throws ConfigurationException {
679: Class[] signature = getMethodSignature(node);
680: if (signature == null)
681: return null;
682:
683: ArrayList jmxSignature = new ArrayList();
684: for (int i = 0; i < signature.length; ++i) {
685: jmxSignature.add(signature[i].getName());
686: }
687: return (String[]) jmxSignature.toArray(new String[jmxSignature
688: .size()]);
689: }
690:
691: private static java.lang.Object[] getMethodArguments(Node node,
692: MBeanServer server) throws ConfigurationException {
693: List children = node.getChildren();
694: if (children == null)
695: return null;
696: ArrayList arguments = new ArrayList();
697: for (int i = 0; i < children.size(); ++i) {
698: Node child = (Node) children.get(i);
699: if (child instanceof Arg) {
700: Arg arg = (Arg) child;
701: arguments.add(arg.configure(server));
702: }
703: }
704: return arguments.toArray();
705: }
706:
707: private static Class loadClass(String className)
708: throws ConfigurationException {
709: try {
710: return Thread.currentThread().getContextClassLoader()
711: .loadClass(className);
712: } catch (ClassNotFoundException x) {
713: throw new ConfigurationException(x);
714: }
715: }
716:
717: private static java.lang.Object getObject(Node node, String key) {
718: while (node != null) {
719: if (node instanceof ObjectsHolder) {
720: ObjectsHolder holder = (ObjectsHolder) node;
721: if (holder.containsKey(key))
722: return holder.getObject(key);
723: }
724: node = node.getParent();
725: }
726: return null;
727: }
728:
729: private static void putObject(Node node, String key,
730: java.lang.Object value) {
731: while (node != null) {
732: if (node instanceof ObjectsHolder) {
733: ObjectsHolder holder = (ObjectsHolder) node;
734: holder.putObject(key, value);
735: }
736: node = node.getParent();
737: }
738: }
739: }
|