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: package org.apache.cocoon.generation;
018:
019: import org.apache.avalon.framework.configuration.Configurable;
020: import org.apache.avalon.framework.configuration.Configuration;
021: import org.apache.avalon.framework.configuration.ConfigurationException;
022: import org.apache.avalon.framework.context.Context;
023: import org.apache.avalon.framework.context.ContextException;
024: import org.apache.avalon.framework.context.Contextualizable;
025: import org.apache.avalon.framework.parameters.Parameters;
026: import org.apache.avalon.framework.service.ServiceException;
027: import org.apache.avalon.framework.service.ServiceManager;
028:
029: import org.apache.cocoon.Constants;
030: import org.apache.cocoon.ProcessingException;
031: import org.apache.cocoon.ResourceNotFoundException;
032: import org.apache.cocoon.components.source.SourceUtil;
033: import org.apache.cocoon.environment.SourceResolver;
034: import org.apache.cocoon.xml.AttributesImpl;
035: import org.apache.cocoon.xml.XMLUtils;
036:
037: import org.apache.commons.lang.SystemUtils;
038: import org.apache.excalibur.source.Source;
039: import org.apache.excalibur.source.SourceException;
040: import org.apache.excalibur.source.TraversableSource;
041: import org.apache.excalibur.store.Store;
042: import org.apache.excalibur.store.StoreJanitor;
043: import org.xml.sax.Attributes;
044: import org.xml.sax.SAXException;
045:
046: import java.io.File;
047: import java.io.IOException;
048: import java.net.InetAddress;
049: import java.net.UnknownHostException;
050: import java.text.DateFormat;
051: import java.util.ArrayList;
052: import java.util.Collection;
053: import java.util.Date;
054: import java.util.Enumeration;
055: import java.util.Iterator;
056: import java.util.List;
057: import java.util.Map;
058: import java.util.Properties;
059: import java.util.Set;
060: import java.util.StringTokenizer;
061: import java.util.TreeSet;
062:
063: /**
064: * @cocoon.sitemap.component.documentation
065: * Generates an XML representation of the current status of Cocoon.
066: *
067: * @cocoon.sitemap.component.name status
068: * @cocoon.sitemap.component.label content
069: * @cocoon.sitemap.component.logger sitemap.generator.status
070: *
071: * @cocoon.sitemap.component.pooling.max 16
072: *
073: * Potted DTD:
074: *
075: * <code>
076: * <!ELEMENT statusinfo (group|value)*>
077: *
078: * <!ATTLIST statusinfo
079: * date CDATA #IMPLIED
080: * host CDATA #IMPLIED
081: * cocoon-version CDATA #IMPLIED
082: * >
083: *
084: * <!ELEMENT group (group|value)*>
085: * <!ATTLIST group
086: * name CDATA #IMPLIED
087: * >
088: *
089: * <!ELEMENT value (line)+>
090: * <!ATTLIST value
091: * name CDATA #REQUIRED
092: *
093: * <!ELEMENT line (#PCDATA)+>
094: * >
095: * </code>
096: *
097: * @author <a href="mailto:paul@luminas.co.uk">Paul Russell</a> (Luminas Limited)
098: * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
099: * @author <a href="mailto:skoechlin@ivision.fr">Sébastien Kœchlin</a> (iVision)
100: * @author <a href="mailto:g-froehlich@gmx.de">Gerhard Froehlich</a>
101: * @version $Id: StatusGenerator.java 433543 2006-08-22 06:22:54Z crossley $
102: */
103: public class StatusGenerator extends ServiceableGenerator implements
104: Contextualizable, Configurable {
105:
106: /**
107: * The XML namespace for the output document.
108: */
109: public static final String NAMESPACE = "http://apache.org/cocoon/status/2.0";
110:
111: /**
112: * The XML namespace for xlink
113: */
114: protected static final String XLINK_NS = "http://www.w3.org/1999/xlink";
115:
116: /**
117: * The namespace prefix for xlink namespace
118: */
119: protected static final String XLINK_PREFIX = "xlink";
120:
121: /**
122: * The component context.
123: */
124: protected Context context;
125:
126: /**
127: * The StoreJanitor used to get cache statistics
128: */
129: protected StoreJanitor storeJanitor;
130:
131: /**
132: * The persistent store
133: */
134: protected Store storePersistent;
135:
136: /**
137: * List & show the contents of WEB/lib
138: */
139: private boolean showLibrary;
140:
141: /**
142: * WEB-INF/lib directory
143: */
144: private Source libDirectory;
145:
146: public void contextualize(Context context) throws ContextException {
147: this .context = context;
148: }
149:
150: public void configure(Configuration configuration)
151: throws ConfigurationException {
152: this .showLibrary = configuration.getChild("show-libraries")
153: .getValueAsBoolean(true);
154: }
155:
156: /**
157: * Set the current <code>ServiceManager</code> instance used by this
158: * <code>Serviceable</code>.
159: * Need to get statistics about cache hits
160: */
161: public void service(ServiceManager manager) throws ServiceException {
162: super .service(manager);
163:
164: if (this .manager.hasService(StoreJanitor.ROLE)) {
165: this .storeJanitor = (StoreJanitor) manager
166: .lookup(StoreJanitor.ROLE);
167: } else {
168: getLogger()
169: .info(
170: "StoreJanitor is not available. Sorry, no cache statistics");
171: }
172:
173: if (this .manager.hasService(Store.PERSISTENT_STORE)) {
174: this .storePersistent = (Store) this .manager
175: .lookup(Store.PERSISTENT_STORE);
176: } else {
177: getLogger()
178: .info(
179: "Persistent Store is not available. Sorry no cache statistics about it.");
180: }
181: }
182:
183: public void setup(SourceResolver resolver, Map objectModel,
184: String src, Parameters par) throws ProcessingException,
185: SAXException, IOException {
186: super .setup(resolver, objectModel, src, par);
187:
188: if (this .showLibrary) {
189: try {
190: this .libDirectory = super .resolver
191: .resolveURI("context://WEB-INF/lib");
192: } catch (SourceException e) {
193: throw SourceUtil.handle(e);
194: }
195: }
196: }
197:
198: /**
199: * @see org.apache.avalon.framework.activity.Disposable#dispose()
200: */
201: public void dispose() {
202: if (this .manager != null) {
203: this .manager.release(this .storePersistent);
204: this .manager.release(this .storeJanitor);
205: this .storePersistent = null;
206: this .storeJanitor = null;
207: }
208:
209: if (this .libDirectory != null) {
210: super .resolver.release(this .libDirectory);
211: this .libDirectory = null;
212: }
213:
214: super .dispose();
215: }
216:
217: /**
218: * Generate the status information in XML format.
219: * @throws SAXException
220: * when there is a problem creating the output SAX events.
221: */
222: public void generate() throws SAXException, ProcessingException {
223:
224: // Start the document and set the namespace.
225: super .contentHandler.startDocument();
226: super .contentHandler.startPrefixMapping("", NAMESPACE);
227: super .contentHandler.startPrefixMapping(XLINK_PREFIX, XLINK_NS);
228:
229: genStatus();
230:
231: // End the document.
232: super .contentHandler.endPrefixMapping(XLINK_PREFIX);
233: super .contentHandler.endPrefixMapping("");
234: super .contentHandler.endDocument();
235: }
236:
237: /**
238: * Generate the main status document.
239: */
240: private void genStatus() throws SAXException, ProcessingException {
241: // Root element.
242:
243: // The current date and time.
244: String dateTime = DateFormat.getDateTimeInstance().format(
245: new Date());
246: String localHost;
247:
248: // The local host.
249: try {
250: localHost = InetAddress.getLocalHost().getHostName();
251: } catch (UnknownHostException e) {
252: getLogger().debug("StatusGenerator:UnknownHost", e);
253: localHost = "";
254: } catch (SecurityException e) {
255: getLogger().debug("StatusGenerator:Security", e);
256: localHost = "";
257: }
258:
259: AttributesImpl atts = new AttributesImpl();
260: atts.addCDATAAttribute(NAMESPACE, "date", dateTime);
261: atts.addCDATAAttribute(NAMESPACE, "host", localHost);
262: atts.addCDATAAttribute(NAMESPACE, "cocoon-version",
263: Constants.VERSION);
264: super .contentHandler.startElement(NAMESPACE, "statusinfo",
265: "statusinfo", atts);
266:
267: genVMStatus();
268: genProperties();
269: if (this .showLibrary) {
270: genLibrarylist();
271: }
272:
273: // End root element.
274: super .contentHandler.endElement(NAMESPACE, "statusinfo",
275: "statusinfo");
276: }
277:
278: private void genVMStatus() throws SAXException {
279: AttributesImpl atts = new AttributesImpl();
280: startGroup("VM");
281:
282: // BEGIN ClassPath
283: String classpath = SystemUtils.JAVA_CLASS_PATH;
284: if (classpath != null) {
285: List paths = new ArrayList();
286: StringTokenizer tokenizer = new StringTokenizer(classpath,
287: SystemUtils.PATH_SEPARATOR);
288: while (tokenizer.hasMoreTokens()) {
289: paths.add(tokenizer.nextToken());
290: }
291: addMultilineValue("classpath", paths);
292: }
293: // END ClassPath
294:
295: // BEGIN CONTEXT CLASSPATH
296: String contextClassPath = null;
297: try {
298: contextClassPath = (String) this .context
299: .get(Constants.CONTEXT_CLASSPATH);
300: } catch (ContextException e) {
301: // we ignore this
302: }
303: if (contextClassPath != null) {
304: List paths = new ArrayList();
305: StringTokenizer tokenizer = new StringTokenizer(
306: contextClassPath, File.pathSeparator);
307: while (tokenizer.hasMoreTokens()) {
308: paths.add(tokenizer.nextToken());
309: }
310: addMultilineValue("context-classpath", paths);
311: }
312: // END CONTEXT CLASSPATH
313:
314: // BEGIN Memory status
315: startGroup("Memory");
316: final long totalMemory = Runtime.getRuntime().totalMemory();
317: final long freeMemory = Runtime.getRuntime().freeMemory();
318: addValue("total", String.valueOf(totalMemory));
319: addValue("used", String.valueOf(totalMemory - freeMemory));
320: addValue("free", String.valueOf(freeMemory));
321: endGroup();
322: // END Memory status
323:
324: // BEGIN JRE
325: startGroup("JRE");
326: addValue("version", SystemUtils.JAVA_VERSION);
327: atts.clear();
328: // qName = prefix + ':' + localName
329: atts.addAttribute(XLINK_NS, "type", XLINK_PREFIX + ":type",
330: "CDATA", "simple");
331: atts.addAttribute(XLINK_NS, "href", XLINK_PREFIX + ":href",
332: "CDATA", SystemUtils.JAVA_VENDOR_URL);
333: addValue("java-vendor", SystemUtils.JAVA_VENDOR, atts);
334: endGroup();
335: // END JRE
336:
337: // BEGIN Operating system
338: startGroup("Operating System");
339: addValue("name", SystemUtils.OS_NAME);
340: addValue("architecture", SystemUtils.OS_ARCH);
341: addValue("version", SystemUtils.OS_VERSION);
342: endGroup();
343: // END operating system
344:
345: // BEGIN Cache
346: if (this .storeJanitor != null) {
347: startGroup("Store Janitor");
348:
349: // For each element in StoreJanitor
350: Iterator i = this .storeJanitor.iterator();
351: while (i.hasNext()) {
352: Store store = (Store) i.next();
353: startGroup(store.getClass().getName() + " (hash = 0x"
354: + Integer.toHexString(store.hashCode()) + ")");
355: int size = 0;
356: int empty = 0;
357: atts.clear();
358: atts.addAttribute(NAMESPACE, "name", "name", "CDATA",
359: "cached");
360: super .contentHandler.startElement(NAMESPACE, "value",
361: "value", atts);
362:
363: atts.clear();
364: Enumeration e = store.keys();
365: while (e.hasMoreElements()) {
366: size++;
367: Object key = e.nextElement();
368: Object val = store.get(key);
369: String line;
370: if (val == null) {
371: empty++;
372: } else {
373: line = key + " (class: "
374: + val.getClass().getName() + ")";
375: super .contentHandler.startElement(NAMESPACE,
376: "line", "line", atts);
377: super .contentHandler.characters(line
378: .toCharArray(), 0, line.length());
379: super .contentHandler.endElement(NAMESPACE,
380: "line", "line");
381: }
382: }
383: if (size == 0) {
384: super .contentHandler.startElement(NAMESPACE,
385: "line", "line", atts);
386: String value = "[empty]";
387: super .contentHandler.characters(
388: value.toCharArray(), 0, value.length());
389: super .contentHandler.endElement(NAMESPACE, "line",
390: "line");
391: }
392: super .contentHandler.endElement(NAMESPACE, "value",
393: "value");
394:
395: addValue("size", String.valueOf(size)
396: + " items in cache (" + empty + " are empty)");
397: endGroup();
398: }
399: endGroup();
400: }
401:
402: if (this .storePersistent != null) {
403: startGroup(storePersistent.getClass().getName()
404: + " (hash = 0x"
405: + Integer.toHexString(storePersistent.hashCode())
406: + ")");
407: int size = 0;
408: int empty = 0;
409: atts.clear();
410: atts.addAttribute(NAMESPACE, "name", "name", "CDATA",
411: "cached");
412: super .contentHandler.startElement(NAMESPACE, "value",
413: "value", atts);
414:
415: atts.clear();
416: Enumeration e = this .storePersistent.keys();
417: while (e.hasMoreElements()) {
418: size++;
419: Object key = e.nextElement();
420: Object val = storePersistent.get(key);
421: String line;
422: if (val == null) {
423: empty++;
424: } else {
425: line = key + " (class: " + val.getClass().getName()
426: + ")";
427: super .contentHandler.startElement(NAMESPACE,
428: "line", "line", atts);
429: super .contentHandler.characters(line.toCharArray(),
430: 0, line.length());
431: super .contentHandler.endElement(NAMESPACE, "line",
432: "line");
433: }
434: }
435: if (size == 0) {
436: super .contentHandler.startElement(NAMESPACE, "line",
437: "line", atts);
438: String value = "[empty]";
439: super .contentHandler.characters(value.toCharArray(), 0,
440: value.length());
441: super .contentHandler.endElement(NAMESPACE, "line",
442: "line");
443: }
444: super .contentHandler
445: .endElement(NAMESPACE, "value", "value");
446:
447: addValue("size", size + " items in cache (" + empty
448: + " are empty)");
449: endGroup();
450: }
451: // END Cache
452:
453: endGroup();
454: }
455:
456: private void genProperties() throws SAXException {
457: this .startGroup("System-Properties");
458: final Properties p = System.getProperties();
459: final Enumeration e = p.keys();
460: while (e.hasMoreElements()) {
461: final String key = (String) e.nextElement();
462: final String value = p.getProperty(key);
463: this .addValue(key, value);
464: }
465: this .endGroup();
466: }
467:
468: private void genLibrarylist() throws SAXException,
469: ProcessingException {
470: try {
471: if (this .libDirectory instanceof TraversableSource) {
472: startGroup("WEB-INF/lib");
473:
474: Set files = new TreeSet();
475: Collection kids = ((TraversableSource) this .libDirectory)
476: .getChildren();
477: try {
478: for (Iterator i = kids.iterator(); i.hasNext();) {
479: final Source lib = (Source) i.next();
480: final String name = lib.getURI().substring(
481: lib.getURI().lastIndexOf('/') + 1);
482: files.add(name);
483: }
484: } finally {
485: for (Iterator i = kids.iterator(); i.hasNext();) {
486: final Source lib = (Source) i.next();
487: super .resolver.release(lib);
488: }
489: }
490:
491: for (Iterator i = files.iterator(); i.hasNext();) {
492: addValue("file", (String) i.next());
493: }
494:
495: endGroup();
496: }
497: } catch (SourceException e) {
498: throw new ResourceNotFoundException(
499: "Could not read directory", e);
500: }
501: }
502:
503: /** Utility function to begin a <code>group</code> tag pair. */
504: private void startGroup(String name) throws SAXException {
505: startGroup(name, null);
506: }
507:
508: /** Utility function to begin a <code>group</code> tag pair with added attributes. */
509: private void startGroup(String name, Attributes atts)
510: throws SAXException {
511: AttributesImpl ai = (atts == null) ? new AttributesImpl()
512: : new AttributesImpl(atts);
513: ai.addAttribute(NAMESPACE, "name", "name", "CDATA", name);
514: super .contentHandler.startElement(NAMESPACE, "group", "group",
515: ai);
516: }
517:
518: /** Utility function to end a <code>group</code> tag pair. */
519: private void endGroup() throws SAXException {
520: super .contentHandler.endElement(NAMESPACE, "group", "group");
521: }
522:
523: /** Utility function to begin and end a <code>value</code> tag pair. */
524: private void addValue(String name, String value)
525: throws SAXException {
526: addValue(name, value, null);
527: }
528:
529: /** Utility function to begin and end a <code>value</code> tag pair with added attributes. */
530: private void addValue(String name, String value, Attributes atts)
531: throws SAXException {
532: AttributesImpl ai = (atts == null) ? new AttributesImpl()
533: : new AttributesImpl(atts);
534: ai.addAttribute(NAMESPACE, "name", "name", "CDATA", name);
535: super .contentHandler.startElement(NAMESPACE, "value", "value",
536: ai);
537: super .contentHandler.startElement(NAMESPACE, "line", "line",
538: XMLUtils.EMPTY_ATTRIBUTES);
539:
540: if (value != null) {
541: super .contentHandler.characters(value.toCharArray(), 0,
542: value.length());
543: }
544:
545: super .contentHandler.endElement(NAMESPACE, "line", "line");
546: super .contentHandler.endElement(NAMESPACE, "value", "value");
547: }
548:
549: /** Utility function to begin and end a <code>value</code> tag pair. */
550: private void addMultilineValue(String name, List values)
551: throws SAXException {
552: addMultilineValue(name, values, null);
553: }
554:
555: /** Utility function to begin and end a <code>value</code> tag pair with added attributes. */
556: private void addMultilineValue(String name, List values,
557: Attributes atts) throws SAXException {
558: AttributesImpl ai = (atts == null) ? new AttributesImpl()
559: : new AttributesImpl(atts);
560: ai.addAttribute(NAMESPACE, "name", "name", "CDATA", name);
561: super .contentHandler.startElement(NAMESPACE, "value", "value",
562: ai);
563:
564: for (int i = 0; i < values.size(); i++) {
565: String value = (String) values.get(i);
566: if (value != null) {
567: super .contentHandler.startElement(NAMESPACE, "line",
568: "line", XMLUtils.EMPTY_ATTRIBUTES);
569: super .contentHandler.characters(value.toCharArray(), 0,
570: value.length());
571: super .contentHandler.endElement(NAMESPACE, "line",
572: "line");
573: }
574: }
575: super .contentHandler.endElement(NAMESPACE, "value", "value");
576: }
577: }
|