001: /*
002: * $Id: APIManager.java,v 1.28 2007/09/18 08:45:07 agoubard Exp $
003: *
004: * Copyright 2003-2007 Orange Nederland Breedband B.V.
005: * See the COPYRIGHT file for redistribution and use restrictions.
006: */
007: package org.xins.server;
008:
009: import java.io.IOException;
010: import java.net.InetAddress;
011: import java.net.UnknownHostException;
012: import java.text.DateFormat;
013: import java.text.SimpleDateFormat;
014: import java.util.HashMap;
015: import java.util.Iterator;
016: import java.util.List;
017: import java.util.Properties;
018:
019: import javax.management.openmbean.CompositeDataSupport;
020: import javax.management.openmbean.CompositeType;
021: import javax.management.openmbean.OpenDataException;
022: import javax.management.openmbean.OpenType;
023: import javax.management.openmbean.SimpleType;
024: import javax.management.openmbean.TabularDataSupport;
025: import javax.management.openmbean.TabularType;
026:
027: import org.apache.log4j.Logger;
028: import org.apache.log4j.jmx.HierarchyDynamicMBean;
029:
030: import org.xins.common.Utils;
031: import org.xins.common.collections.PropertyReaderConverter;
032: import org.xins.common.collections.PropertyReaderUtils;
033: import org.xins.common.text.DateConverter;
034: import org.xins.common.text.TextUtils;
035: import org.xins.common.xml.Element;
036:
037: /**
038: * Management bean for the API.
039: *
040: * @version $Revision: 1.28 $ $Date: 2007/09/18 08:45:07 $
041: * @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
042: *
043: * @since XINS 1.5.0
044: */
045: public final class APIManager implements APIManagerMBean {
046:
047: /**
048: * Formatter to convert {@link String} to {@link java.util.Date}.
049: */
050: private static final DateFormat DATE_FORMATTER = new SimpleDateFormat(
051: "yyyy.MM.DD HH:MM:ss.SSS");
052:
053: /**
054: * The API, never <code>null</code>.
055: */
056: private final API _api;
057:
058: /**
059: * The IP address runing this class, never <code>null</code>.
060: */
061: private String _ip;
062:
063: /**
064: * Ctreates a new API manager MBean.
065: *
066: * @param api
067: * the APi that is managed by this MBean.
068: */
069: APIManager(API api) {
070: _api = api;
071: try {
072: _ip = InetAddress.getLocalHost().getHostAddress();
073: } catch (UnknownHostException uhex) {
074: Log.log_3250(uhex);
075: _ip = "127.0.0.1";
076: }
077: }
078:
079: /**
080: * Gets the version of the API.
081: *
082: * @return
083: * the version of the API running.
084: *
085: * @throws IOException
086: * if the connection to the MBean fails.
087: */
088: public String getAPIVersion() throws IOException {
089: return _api.getBootstrapProperties().get(
090: API.API_VERSION_PROPERTY);
091: }
092:
093: /**
094: * Gets the version of XINS which is running this API.
095: *
096: * @return
097: * the version of XINS running the API.
098: *
099: * @throws IOException
100: * if the connection to the MBean fails.
101: */
102: public String getXINSVersion() throws IOException {
103: return Library.getVersion();
104: }
105:
106: /**
107: * Gets the name of the API.
108: *
109: * @return
110: * the name the API.
111: *
112: * @throws IOException
113: * if the connection to the MBean fails.
114: */
115: public String getAPIName() throws IOException {
116: return _api.getName();
117: }
118:
119: /**
120: * Gets the bootstrap properties.
121: *
122: * @return
123: * the bootstrap properties for this API.
124: *
125: * @throws IOException
126: * if the connection to the MBean fails.
127: */
128: public CompositeDataSupport getBootstrapProperties()
129: throws IOException {
130: Properties bootstrapProps = PropertyReaderConverter
131: .toProperties(_api.getBootstrapProperties());
132: return propertiesToCompositeData(bootstrapProps);
133: }
134:
135: /**
136: * Gets the runtime properties.
137: *
138: * @return
139: * the runtime properties for this API.
140: *
141: * @throws IOException
142: * if the connection to the MBean fails.
143: */
144: public CompositeDataSupport getRuntimeProperties()
145: throws IOException {
146: Properties runtimeProps = PropertyReaderConverter
147: .toProperties(_api.getRuntimeProperties());
148: return propertiesToCompositeData(runtimeProps);
149: }
150:
151: /**
152: * Gets the time at which the API was started.
153: *
154: * @return
155: * the time at which the API was started in the form YYYYMMDDThhmmssSSS+TZ.
156: *
157: * @throws IOException
158: * if the connection to the MBean fails.
159: */
160: public String getStartupTime() throws IOException {
161: return DateConverter.toDateString(_api.getStartupTimestamp());
162: }
163:
164: /**
165: * Gets the list of the API functions.
166: *
167: * @return
168: * the list of the API function names.
169: *
170: * @throws IOException
171: * if the connection to the MBean fails.
172: */
173: public String[] getFunctionNames() throws IOException {
174: List functions = _api.getFunctionList();
175: String[] functionNames = new String[functions.size()];
176: for (int i = 0; i < functions.size(); i++) {
177: Function nextFunction = (Function) functions.get(i);
178: functionNames[i] = nextFunction.getName();
179: }
180: return functionNames;
181: }
182:
183: /**
184: * Gets the statistics of the functions.
185: *
186: * @return
187: * the statistics of the functions.
188: *
189: * @throws IOException
190: * if the connection to the MBean fails.
191: */
192: public TabularDataSupport getStatistics() throws IOException {
193: String[] statsNames = { "Function", "Count", "Error Code",
194: "Average", "Min Date", "Min Duration", "Max Date",
195: "Max Duration", "Last Date", "Last Duration" };
196: OpenType[] statsTypes = { SimpleType.STRING, SimpleType.LONG,
197: SimpleType.STRING, SimpleType.LONG, SimpleType.DATE,
198: SimpleType.LONG, SimpleType.DATE, SimpleType.LONG,
199: SimpleType.DATE, SimpleType.LONG };
200: try {
201: CompositeType statType = new CompositeType("Statistic",
202: "A statistic of a function", statsNames,
203: statsNames, statsTypes);
204: TabularType tabType = new TabularType(
205: "Function statistics",
206: "Statistics of the functions", statType, statsNames);
207: TabularDataSupport tabularData = new TabularDataSupport(
208: tabType);
209: Iterator itFunctions = _api.getFunctionList().iterator();
210: while (itFunctions.hasNext()) {
211: Function nextFunction = (Function) itFunctions.next();
212: Element success = nextFunction.getStatistics()
213: .getSuccessfulElement();
214: HashMap statMap = statisticsToMap(success, nextFunction
215: .getName());
216: CompositeDataSupport statData = new CompositeDataSupport(
217: statType, statMap);
218: tabularData.put(statData);
219: Element[] unsuccess = nextFunction.getStatistics()
220: .getUnsuccessfulElement(true);
221: for (int i = 0; i < unsuccess.length; i++) {
222: HashMap statMap2 = statisticsToMap(unsuccess[i],
223: nextFunction.getName());
224: CompositeDataSupport statData2 = new CompositeDataSupport(
225: statType, statMap2);
226: tabularData.put(statData2);
227: }
228: }
229:
230: return tabularData;
231: } catch (OpenDataException odex) {
232: Utils.logProgrammingError(odex);
233: return null;
234: }
235: }
236:
237: /**
238: * Executes the _NoOp meta function.
239: *
240: * @throws IOException
241: * if the connection to the MBean fails.
242: *
243: * @throws NoSuchFunctionException
244: * if the _noOp meta function is not found.
245: *
246: * @throws AccessDeniedException
247: * if the JMX client is not in the ACLs to execute the _noOp meta function.
248: */
249: public void noOp() throws IOException, NoSuchFunctionException,
250: AccessDeniedException {
251: FunctionRequest noOpRequest = new FunctionRequest("_NoOp",
252: PropertyReaderUtils.EMPTY_PROPERTY_READER, null);
253: _api.handleCall(System.currentTimeMillis(), noOpRequest, _ip,
254: null);
255: }
256:
257: /**
258: * Reloads the runtime properties if the file has changed.
259: *
260: * @throws IOException
261: * if the connection to the MBean fails.
262: *
263: * @throws NoSuchFunctionException
264: * if the _ReloadProperties meta function is not found.
265: *
266: * @throws AccessDeniedException
267: * if the JMX client is not in the ACLs to execute the _ReloadProperties meta function.
268: */
269: public void reloadProperties() throws IOException,
270: NoSuchFunctionException, AccessDeniedException {
271: FunctionRequest reloadPropertiesRequest = new FunctionRequest(
272: "_ReloadProperties",
273: PropertyReaderUtils.EMPTY_PROPERTY_READER, null);
274: _api.handleCall(System.currentTimeMillis(),
275: reloadPropertiesRequest, _ip, null);
276: }
277:
278: /**
279: * Put the data of a function statistic in a {@link HashMap}.
280: *
281: * @param statElement
282: * the XML element containing the data about the successful or unsuccessful call,
283: * cannot be <code>null</code>.
284: *
285: * @param functionName
286: * the name of the function of this statistic, cannot be <code>null</code>.
287: *
288: * @return
289: * a {@link HashMap} containing the statistics.
290: */
291: private HashMap statisticsToMap(Element statElement,
292: String functionName) {
293: HashMap statMap = new HashMap();
294: statMap.put("Function", functionName);
295: statMap.put("Count",
296: new Long(statElement.getAttribute("count")));
297: if (!TextUtils.isEmpty(statElement.getAttribute("errorcode"))) {
298: statMap.put("Error Code", statElement
299: .getAttribute("errorcode"));
300: } else if (statElement.getLocalName().equals("unsuccessful")) {
301: statMap.put("Error Code", "<unsuccessful>");
302: } else if (statElement.getLocalName().equals("successful")) {
303: statMap.put("Error Code", "<successful>");
304: }
305: if (!"N/A".equals(statElement.getAttribute("average"))) {
306: statMap.put("Average", new Long(statElement
307: .getAttribute("average")));
308: } else {
309: statMap.put("Average", null);
310: }
311: try {
312: Element minStat = statElement.getUniqueChildElement("min");
313: if (!"N/A".equals(minStat.getAttribute("duration"))) {
314: synchronized (DATE_FORMATTER) {
315: statMap.put("Min Date", DATE_FORMATTER
316: .parse(minStat.getAttribute("start")));
317: }
318: statMap.put("Min Duration", new Long(minStat
319: .getAttribute("duration")));
320: } else {
321: statMap.put("Min Date", null);
322: statMap.put("Min Duration", null);
323: }
324: } catch (Exception ex) {
325: Utils.logProgrammingError(ex);
326: }
327: try {
328: Element maxStat = statElement.getUniqueChildElement("max");
329: if (!"N/A".equals(maxStat.getAttribute("duration"))) {
330: synchronized (DATE_FORMATTER) {
331: statMap.put("Max Date", DATE_FORMATTER
332: .parse(maxStat.getAttribute("start")));
333: }
334: statMap.put("Max Duration", new Long(maxStat
335: .getAttribute("duration")));
336: } else {
337: statMap.put("Max Date", null);
338: statMap.put("Max Duration", null);
339: }
340: } catch (Exception ex) {
341: Utils.logProgrammingError(ex);
342: }
343: try {
344: Element lastStat = statElement
345: .getUniqueChildElement("last");
346: if (!"N/A".equals(lastStat.getAttribute("duration"))) {
347: synchronized (DATE_FORMATTER) {
348: statMap.put("Last Date", DATE_FORMATTER
349: .parse(lastStat.getAttribute("start")));
350: }
351: statMap.put("Last Duration", new Long(lastStat
352: .getAttribute("duration")));
353: } else {
354: statMap.put("Last Date", null);
355: statMap.put("Last Duration", null);
356: }
357: } catch (Exception ex) {
358: Utils.logProgrammingError(ex);
359: }
360:
361: return statMap;
362: }
363:
364: /**
365: * Utility method to convert a {@link Properties} to a {@link CompositeDataSupport}.
366: *
367: * @param properties
368: * the properties to represent to the JMX agent, cannot be <code>null</code>.
369: *
370: * @return
371: * the {@link CompositeDataSupport} containng the properties, or <code>null</code>
372: * if an error occured.
373: */
374: private CompositeDataSupport propertiesToCompositeData(
375: Properties properties) {
376: try {
377: String[] keys = (String[]) properties.keySet().toArray(
378: new String[properties.size()]);
379: OpenType[] itemTypes = new OpenType[keys.length];
380: for (int i = 0; i < itemTypes.length; i++) {
381: itemTypes[i] = SimpleType.STRING;
382: }
383: CompositeType propsType = new CompositeType(
384: "Properties type", "properties", keys, keys,
385: itemTypes);
386: CompositeDataSupport propsData = new CompositeDataSupport(
387: propsType, properties);
388: return propsData;
389: } catch (OpenDataException odex) {
390: Utils.logProgrammingError(odex);
391: return null;
392: }
393: }
394:
395: /**
396: * Registers the API MBean.
397: *
398: * @param api
399: * the API, never <code>null</code>.
400: *
401: * @throws Throwable
402: * if the MBeanServer cannot be found or created or one of the registered MBean fails.
403: */
404: static void registerMBean(API api) throws Throwable {
405: javax.management.MBeanServer mBeanServer;
406: try {
407: mBeanServer = (javax.management.MBeanServer) Class.forName(
408: "java.lang.management.ManagementFactory")
409: .getMethod("getPlatformMBeanServer", null).invoke(
410: null, null);
411: } catch (ClassNotFoundException cnfe) {
412:
413: // Try with the JDK 1.4 and 1.3 compatible JMX reference implementation
414: mBeanServer = javax.management.MBeanServerFactory
415: .createMBeanServer();
416: }
417: APIManager mBean = new APIManager(api);
418: javax.management.ObjectName objectName = new javax.management.ObjectName(
419: "org.xins.server.api." + api.getName()
420: + ":type=APIManager");
421:
422: mBeanServer.registerMBean(mBean, objectName);
423:
424: // Register also the Log4J loggers
425:
426: // Create and Register the top level Log4J MBean
427: HierarchyDynamicMBean hdm = new HierarchyDynamicMBean();
428: javax.management.ObjectName mbo = new javax.management.ObjectName(
429: "org.xins.server.api." + api.getName()
430: + ":hiearchy=log4j");
431: mBeanServer.registerMBean(hdm, mbo);
432:
433: // Add the root logger to the Hierarchy MBean
434: Logger rootLogger = Logger.getRootLogger();
435: hdm.addLoggerMBean(rootLogger.getName());
436: }
437: }
|