001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.ant.jmx;
019:
020: import java.io.IOException;
021: import java.lang.reflect.Array;
022: import java.net.InetAddress;
023: import java.net.MalformedURLException;
024: import java.net.UnknownHostException;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Map;
029: import java.util.Properties;
030: import java.util.Set;
031: import java.util.StringTokenizer;
032:
033: import javax.management.MBeanServerConnection;
034: import javax.management.MalformedObjectNameException;
035: import javax.management.ObjectName;
036: import javax.management.openmbean.CompositeData;
037: import javax.management.openmbean.CompositeDataSupport;
038: import javax.management.openmbean.CompositeType;
039: import javax.management.openmbean.OpenType;
040: import javax.management.openmbean.SimpleType;
041: import javax.management.openmbean.TabularDataSupport;
042: import javax.management.remote.JMXConnector;
043: import javax.management.remote.JMXConnectorFactory;
044: import javax.management.remote.JMXServiceURL;
045:
046: import org.apache.catalina.ant.BaseRedirectorHelperTask;
047: import org.apache.tools.ant.BuildException;
048: import org.apache.tools.ant.Project;
049:
050: /**
051: * Access <em>JMX</em> JSR 160 MBeans Server.
052: * <ul>
053: * <li>open more then one JSR 160 rmi connection</li>
054: * <li>Get/Set Mbeans attributes</li>
055: * <li>Call Mbean Operation with arguments</li>
056: * <li>Argument values can be converted from string to
057: * int,long,float,double,boolean,ObjectName or InetAddress</li>
058: * <li>Query Mbeans</li>
059: * <li>Show Get, Call, Query result at Ant console log</li>
060: * <li>Bind Get, Call, Query result at Ant properties</li>
061: * </ul>
062: *
063: * Examples: open server with reference and autorisation
064: *
065: * <pre>
066: *
067: * <jmxOpen
068: * host="127.0.0.1"
069: * port="9014"
070: * username="monitorRole"
071: * password="mysecret"
072: * ref="jmx.myserver"
073: * />
074: *
075: * </pre>
076: *
077: * All calls after opening with same refid reuse the connection.
078: * <p>
079: * First call to a remote MBeanserver save the JMXConnection a referenz
080: * <em>jmx.server</em>
081: * </p>
082: * All JMXAccessorXXXTask support the attribute <em>if</em> and
083: * <em>unless</em>. With <em>if</em> the task is only execute when property
084: * exist and with <em>unless</em> when property not exists. <br/><b>NOTE
085: * </b>: These tasks require Ant 1.6 or later interface.
086: *
087: * @author Peter Rossbach
088: * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
089: * @since 5.5.10
090: */
091:
092: public class JMXAccessorTask extends BaseRedirectorHelperTask {
093:
094: // ----------------------------------------------------- Instance Variables
095:
096: public static String JMX_SERVICE_PREFIX = "service:jmx:rmi:///jndi/rmi://";
097:
098: public static String JMX_SERVICE_SUFFIX = "/jmxrmi";
099:
100: private String name = null;
101:
102: private String resultproperty;
103:
104: private String url = null;
105:
106: private String host = "localhost";
107:
108: private String port = "8050";
109:
110: private String password = null;
111:
112: private String username = null;
113:
114: private String ref = "jmx.server";
115:
116: private boolean echo = false;
117:
118: private boolean separatearrayresults = true;
119:
120: private String delimiter;
121:
122: private String unlessCondition;
123:
124: private String ifCondition;
125:
126: private Properties properties = new Properties();
127:
128: // ----------------------------------------------------- Instance Info
129:
130: /**
131: * Descriptive information describing this implementation.
132: */
133: private static final String info = "org.apache.catalina.ant.JMXAccessorTask/1.1";
134:
135: /**
136: * Return descriptive information about this implementation and the
137: * corresponding version number, in the format
138: * <code><description>/<version></code>.
139: */
140: public String getInfo() {
141:
142: return (info);
143:
144: }
145:
146: // ------------------------------------------------------------- Properties
147:
148: /**
149: * The name used at remote MbeanServer
150: */
151:
152: public String getName() {
153: return (this .name);
154: }
155:
156: public void setName(String objectName) {
157: this .name = objectName;
158: }
159:
160: /**
161: * @return Returns the resultproperty.
162: */
163: public String getResultproperty() {
164: return resultproperty;
165: }
166:
167: /**
168: * @param propertyName The resultproperty to set.
169: */
170: public void setResultproperty(String propertyName) {
171: this .resultproperty = propertyName;
172: }
173:
174: /**
175: * @return Returns the delimiter.
176: */
177: public String getDelimiter() {
178: return delimiter;
179: }
180:
181: /**
182: * @param separator The delimiter to set.
183: */
184: public void setDelimiter(String separator) {
185: this .delimiter = separator;
186: }
187:
188: /**
189: * @return Returns the echo.
190: */
191: public boolean isEcho() {
192: return echo;
193: }
194:
195: /**
196: * @param echo
197: * The echo to set.
198: */
199: public void setEcho(boolean echo) {
200: this .echo = echo;
201: }
202:
203: /**
204: * @return Returns the separatearrayresults.
205: */
206: public boolean isSeparatearrayresults() {
207: return separatearrayresults;
208: }
209:
210: /**
211: * @param separateArrayResults
212: * The separatearrayresults to set.
213: */
214: public void setSeparatearrayresults(boolean separateArrayResults) {
215: this .separatearrayresults = separateArrayResults;
216: }
217:
218: /**
219: * The login password for the <code>Manager</code> application.
220: */
221: public String getPassword() {
222: return (this .password);
223: }
224:
225: public void setPassword(String password) {
226: this .password = password;
227: }
228:
229: /**
230: * The login username for the <code>JMX</code> MBeanServer.
231: */
232: public String getUsername() {
233: return (this .username);
234: }
235:
236: public void setUsername(String username) {
237: this .username = username;
238: }
239:
240: /**
241: * The URL of the <code>JMX JSR 160</code> MBeanServer to be used.
242: */
243:
244: public String getUrl() {
245: return (this .url);
246: }
247:
248: public void setUrl(String url) {
249: this .url = url;
250: }
251:
252: /**
253: * The Host of the <code>JMX JSR 160</code> MBeanServer to be used.
254: */
255:
256: public String getHost() {
257: return (this .host);
258: }
259:
260: public void setHost(String host) {
261: this .host = host;
262: }
263:
264: /**
265: * The Port of the <code>JMX JSR 160</code> MBeanServer to be used.
266: */
267:
268: public String getPort() {
269: return (this .port);
270: }
271:
272: public void setPort(String port) {
273: this .port = port;
274: }
275:
276: /**
277: * @return Returns the useRef.
278: */
279: public boolean isUseRef() {
280: return ref != null && !"".equals(ref);
281: }
282:
283: /**
284: * @return Returns the ref.
285: */
286: public String getRef() {
287: return ref;
288: }
289:
290: /**
291: * @param refId The ref to set.
292: */
293: public void setRef(String refId) {
294: this .ref = refId;
295: }
296:
297: /**
298: * @return Returns the ifCondition.
299: */
300: public String getIf() {
301: return ifCondition;
302: }
303:
304: /**
305: * Only execute if a property of the given name exists in the current
306: * project.
307: *
308: * @param c property name
309: */
310: public void setIf(String c) {
311: ifCondition = c;
312: }
313:
314: /**
315: * @return Returns the unlessCondition.
316: */
317: public String getUnless() {
318: return unlessCondition;
319: }
320:
321: /**
322: * Only execute if a property of the given name does not exist in the
323: * current project.
324: *
325: * @param c property name
326: */
327: public void setUnless(String c) {
328: unlessCondition = c;
329: }
330:
331: // --------------------------------------------------------- Public Methods
332:
333: /**
334: * Execute the specified command. This logic only performs the common
335: * attribute validation required by all subclasses; it does not perform any
336: * functional logic directly.
337: *
338: * @exception BuildException
339: * if a validation error occurs
340: */
341: public void execute() throws BuildException {
342: if (testIfCondition() && testUnlessCondition()) {
343: try {
344: String error = null;
345:
346: MBeanServerConnection jmxServerConnection = getJMXConnection();
347: error = jmxExecute(jmxServerConnection);
348: if (error != null && isFailOnError()) {
349: // exception should be thrown only if failOnError == true
350: // or error line will be logged twice
351: throw new BuildException(error);
352: }
353: } catch (Throwable t) {
354: if (isFailOnError()) {
355: throw new BuildException(t);
356: } else {
357: handleErrorOutput(t.getMessage());
358: }
359: } finally {
360: closeRedirector();
361: }
362: }
363: }
364:
365: /**
366: * create a new JMX Connection with auth when username and password is set.
367: */
368: public static MBeanServerConnection createJMXConnection(String url,
369: String host, String port, String username, String password)
370: throws MalformedURLException, IOException {
371: String urlForJMX;
372: if (url != null)
373: urlForJMX = url;
374: else
375: urlForJMX = JMX_SERVICE_PREFIX + host + ":" + port
376: + JMX_SERVICE_SUFFIX;
377: Map environment = null;
378: if (username != null && password != null) {
379: String[] credentials = new String[2];
380: credentials[0] = username;
381: credentials[1] = password;
382: environment = new HashMap();
383: environment.put(JMXConnector.CREDENTIALS, credentials);
384: }
385: return JMXConnectorFactory.connect(
386: new JMXServiceURL(urlForJMX), environment)
387: .getMBeanServerConnection();
388:
389: }
390:
391: /**
392: * test the if condition
393: *
394: * @return true if there is no if condition, or the named property exists
395: */
396: protected boolean testIfCondition() {
397: if (ifCondition == null || "".equals(ifCondition)) {
398: return true;
399: }
400: return getProperty(ifCondition) != null;
401: }
402:
403: /**
404: * test the unless condition
405: *
406: * @return true if there is no unless condition, or there is a named
407: * property but it doesn't exist
408: */
409: protected boolean testUnlessCondition() {
410: if (unlessCondition == null || "".equals(unlessCondition)) {
411: return true;
412: }
413: return getProperty(unlessCondition) == null;
414: }
415:
416: /**
417: * Get Current Connection from <em>ref</em> parameter or create a new one!
418: *
419: * @return The server connection
420: * @throws MalformedURLException
421: * @throws IOException
422: */
423: public static MBeanServerConnection accessJMXConnection(
424: Project project, String url, String host, String port,
425: String username, String password, String refId)
426: throws MalformedURLException, IOException {
427: MBeanServerConnection jmxServerConnection = null;
428: boolean isRef = project != null && refId != null
429: && refId.length() > 0;
430: if (isRef) {
431: Object pref = project.getReference(refId);
432: try {
433: jmxServerConnection = (MBeanServerConnection) pref;
434: } catch (ClassCastException cce) {
435: if (project != null) {
436: project.log("wrong object reference " + refId
437: + " - " + pref.getClass());
438: }
439: return null;
440: }
441: }
442: if (jmxServerConnection == null) {
443: jmxServerConnection = createJMXConnection(url, host, port,
444: username, password);
445: }
446: if (isRef && jmxServerConnection != null) {
447: project.addReference(refId, jmxServerConnection);
448: }
449: return jmxServerConnection;
450: }
451:
452: // ------------------------------------------------------ protected Methods
453:
454: /**
455: * get JMXConnection
456: *
457: * @return The connection
458: * @throws MalformedURLException
459: * @throws IOException
460: */
461: protected MBeanServerConnection getJMXConnection()
462: throws MalformedURLException, IOException {
463:
464: MBeanServerConnection jmxServerConnection = null;
465: if (isUseRef()) {
466: Object pref = null;
467: if (getProject() != null) {
468: pref = getProject().getReference(getRef());
469: if (pref != null) {
470: try {
471: jmxServerConnection = (MBeanServerConnection) pref;
472: } catch (ClassCastException cce) {
473: getProject().log(
474: "Wrong object reference " + getRef()
475: + " - " + pref.getClass());
476: return null;
477: }
478: }
479: }
480: if (jmxServerConnection == null) {
481: jmxServerConnection = accessJMXConnection(getProject(),
482: getUrl(), getHost(), getPort(), getUsername(),
483: getPassword(), getRef());
484: }
485: } else {
486: jmxServerConnection = accessJMXConnection(getProject(),
487: getUrl(), getHost(), getPort(), getUsername(),
488: getPassword(), null);
489: }
490: return jmxServerConnection;
491: }
492:
493: /**
494: * Execute the specified command, based on the configured properties. The
495: * input stream will be closed upon completion of this task, whether it was
496: * executed successfully or not.
497: *
498: * @exception Exception
499: * if an error occurs
500: */
501: public String jmxExecute(MBeanServerConnection jmxServerConnection)
502: throws Exception {
503:
504: if ((jmxServerConnection == null)) {
505: throw new BuildException("Must open a connection!");
506: } else if (isEcho()) {
507: handleOutput("JMX Connection ref=" + ref + " is open!");
508: }
509: return null;
510: }
511:
512: /**
513: * Convert string to datatype FIXME How we can transfer values from ant
514: * project reference store (ref)?
515: *
516: * @param value The value
517: * @param valueType The type
518: * @return The converted object
519: */
520: protected Object convertStringToType(String value, String valueType) {
521: if ("java.lang.String".equals(valueType))
522: return value;
523:
524: Object convertValue = value;
525: if ("java.lang.Integer".equals(valueType)
526: || "int".equals(valueType)) {
527: try {
528: convertValue = new Integer(value);
529: } catch (NumberFormatException ex) {
530: if (isEcho())
531: handleErrorOutput("Unable to convert to integer:"
532: + value);
533: }
534: } else if ("java.lang.Long".equals(valueType)
535: || "long".equals(valueType)) {
536: try {
537: convertValue = new Long(value);
538: } catch (NumberFormatException ex) {
539: if (isEcho())
540: handleErrorOutput("Unable to convert to long:"
541: + value);
542: }
543: } else if ("java.lang.Boolean".equals(valueType)
544: || "boolean".equals(valueType)) {
545: convertValue = new Boolean(value);
546: } else if ("java.lang.Float".equals(valueType)
547: || "float".equals(valueType)) {
548: try {
549: convertValue = new Float(value);
550: } catch (NumberFormatException ex) {
551: if (isEcho())
552: handleErrorOutput("Unable to convert to float:"
553: + value);
554: }
555: } else if ("java.lang.Double".equals(valueType)
556: || "double".equals(valueType)) {
557: try {
558: convertValue = new Double(value);
559: } catch (NumberFormatException ex) {
560: if (isEcho())
561: handleErrorOutput("Unable to convert to double:"
562: + value);
563: }
564: } else if ("javax.management.ObjectName".equals(valueType)
565: || "name".equals(valueType)) {
566: try {
567: convertValue = new ObjectName(value);
568: } catch (MalformedObjectNameException e) {
569: if (isEcho())
570: handleErrorOutput("Unable to convert to ObjectName:"
571: + value);
572: }
573: } else if ("java.net.InetAddress".equals(valueType)) {
574: try {
575: convertValue = InetAddress.getByName(value);
576: } catch (UnknownHostException exc) {
577: if (isEcho())
578: handleErrorOutput("Unable to resolve host name:"
579: + value);
580: }
581: }
582: return convertValue;
583: }
584:
585: /**
586: * @param name context of result
587: * @param result
588: */
589: protected void echoResult(String name, Object result) {
590: if (isEcho()) {
591: if (result.getClass().isArray()) {
592: for (int i = 0; i < Array.getLength(result); i++) {
593: handleOutput(name + "." + i + "="
594: + Array.get(result, i));
595: }
596: } else
597: handleOutput(name + "=" + result);
598: }
599: }
600:
601: /**
602: * create result as property with name from attribute resultproperty
603: *
604: * @param result The result
605: * @see #createProperty(String, Object)
606: */
607: protected void createProperty(Object result) {
608: if (resultproperty != null) {
609: createProperty(resultproperty, result);
610: }
611: }
612:
613: /**
614: * create result as property with name from property prefix When result is
615: * an array and isSeparateArrayResults is true, resultproperty used as
616: * prefix (<code>resultproperty.0-array.length</code> and store the
617: * result array length at <code>resultproperty.length</code>. Other
618: * option is that you delemit your result with a delimiter
619: * (java.util.StringTokenizer is used).
620: *
621: * @param propertyPrefix
622: * @param result
623: */
624: protected void createProperty(String propertyPrefix, Object result) {
625: if (propertyPrefix == null)
626: propertyPrefix = "";
627: if (result instanceof CompositeDataSupport) {
628: CompositeDataSupport data = (CompositeDataSupport) result;
629: CompositeType compositeType = data.getCompositeType();
630: Set keys = compositeType.keySet();
631: for (Iterator iter = keys.iterator(); iter.hasNext();) {
632: String key = (String) iter.next();
633: Object value = data.get(key);
634: OpenType type = compositeType.getType(key);
635: if (type instanceof SimpleType) {
636: setProperty(propertyPrefix + "." + key, value);
637: } else {
638: createProperty(propertyPrefix + "." + key, value);
639: }
640: }
641: } else if (result instanceof TabularDataSupport) {
642: TabularDataSupport data = (TabularDataSupport) result;
643: for (Iterator iter = data.keySet().iterator(); iter
644: .hasNext();) {
645: Object key = iter.next();
646: for (Iterator iter1 = ((List) key).iterator(); iter1
647: .hasNext();) {
648: Object key1 = iter1.next();
649: CompositeData valuedata = data
650: .get(new Object[] { key1 });
651: Object value = valuedata.get("value");
652: OpenType type = valuedata.getCompositeType()
653: .getType("value");
654: if (type instanceof SimpleType) {
655: setProperty(propertyPrefix + "." + key1, value);
656: } else {
657: createProperty(propertyPrefix + "." + key1,
658: value);
659: }
660: }
661: }
662: } else if (result.getClass().isArray()) {
663: if (isSeparatearrayresults()) {
664: int size = 0;
665: for (int i = 0; i < Array.getLength(result); i++) {
666: if (setProperty(propertyPrefix + "." + size, Array
667: .get(result, i))) {
668: size++;
669: }
670: }
671: if (size > 0) {
672: setProperty(propertyPrefix + ".Length", Integer
673: .toString(size));
674: }
675: }
676: } else {
677: String delim = getDelimiter();
678: if (delim != null) {
679: StringTokenizer tokenizer = new StringTokenizer(result
680: .toString(), delim);
681: int size = 0;
682: for (; tokenizer.hasMoreTokens();) {
683: String token = tokenizer.nextToken();
684: if (setProperty(propertyPrefix + "." + size, token)) {
685: size++;
686: }
687: }
688: if (size > 0)
689: setProperty(propertyPrefix + ".Length", Integer
690: .toString(size));
691: } else {
692: setProperty(propertyPrefix, result.toString());
693: }
694: }
695: }
696:
697: /**
698: * get all properties, when project is there got all project Properties
699: * @return properties
700: */
701: public Map getProperties() {
702: Project currentProject = getProject();
703: if (currentProject != null) {
704: return currentProject.getProperties();
705: } else {
706: return properties;
707: }
708: }
709:
710: /**
711: * get all Properties
712: * @param property
713: * @return The property
714: */
715: public String getProperty(String property) {
716: Project currentProject = getProject();
717: if (currentProject != null) {
718: return currentProject.getProperty(property);
719: } else {
720: return properties.getProperty(property);
721: }
722: }
723:
724: /**
725: * @param property The property
726: * @param value The value
727: * @return True if successful
728: */
729: public boolean setProperty(String property, Object value) {
730: if (property != null) {
731: if (value == null)
732: value = "";
733: if (isEcho()) {
734: handleOutput(property + "=" + value.toString());
735: }
736: Project currentProject = getProject();
737: if (currentProject != null) {
738: currentProject.setNewProperty(property, value
739: .toString());
740: } else {
741: properties.setProperty(property, value.toString());
742: }
743: return true;
744: }
745: return false;
746: }
747: }
|