/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.generation; import org.apache.avalon.framework.configuration.Configurable; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.context.Context; import org.apache.avalon.framework.context.ContextException; import org.apache.avalon.framework.context.Contextualizable; import org.apache.avalon.framework.parameters.Parameters; import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.cocoon.Constants; import org.apache.cocoon.ProcessingException; import org.apache.cocoon.ResourceNotFoundException; import org.apache.cocoon.components.flow.ContinuationsManager; import org.apache.cocoon.components.flow.WebContinuation; import org.apache.cocoon.components.flow.WebContinuationDataBean; import org.apache.cocoon.components.source.SourceUtil; import org.apache.cocoon.environment.SourceResolver; import org.apache.cocoon.xml.AttributesImpl; import org.apache.cocoon.xml.XMLUtils; import org.apache.commons.lang.SystemUtils; import org.apache.excalibur.source.Source; import org.apache.excalibur.source.SourceException; import org.apache.excalibur.source.TraversableSource; import org.apache.excalibur.store.Store; import org.apache.excalibur.store.StoreJanitor; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; /** * @cocoon.sitemap.component.documentation * Generates an XML representation of the current status of Cocoon. * * @cocoon.sitemap.component.name status * @cocoon.sitemap.component.label content * @cocoon.sitemap.component.logger sitemap.generator.status * * @cocoon.sitemap.component.pooling.max 16 * * Potted DTD: * * <code> * <!ELEMENT statusinfo (group|value)*> * * <!ATTLIST statusinfo * date CDATA #IMPLIED * host CDATA #IMPLIED * cocoon-version CDATA #IMPLIED * > * * <!ELEMENT group (group|value)*> * <!ATTLIST group * name CDATA #IMPLIED * > * * <!ELEMENT value (line)+> * <!ATTLIST value * name CDATA #REQUIRED * * <!ELEMENT line (#PCDATA)+> * > * </code> * * @author <a href="mailto:paul@luminas.co.uk">Paul Russell</a> (Luminas Limited) * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a> * @author <a href="mailto:skoechlin@ivision.fr">Sébastien Kœchlin</a> (iVision) * @author <a href="mailto:g-froehlich@gmx.de">Gerhard Froehlich</a> * @version $Id$ */ public class StatusGenerator extends ServiceableGenerator implements Contextualizable, Configurable { /** * The XML namespace for the output document. */ public static final String NAMESPACE = "http://apache.org/cocoon/status/2.0"; /** * The XML namespace for xlink */ protected static final String XLINK_NS = "http://www.w3.org/1999/xlink"; /** * The namespace prefix for xlink namespace */ protected static final String XLINK_PREFIX = "xlink"; /** * The component context. */ protected Context context; /** * The StoreJanitor used to get cache statistics */ protected StoreJanitor storeJanitor; /** * The persistent store */ protected Store storePersistent; /** * Show continuations information */ private boolean showContinuations; /** * The ContinuationManager */ private ContinuationsManager continuationsManager; /** * List & show the contents of WEB/lib */ private boolean showLibrary; /** * WEB-INF/lib directory */ private Source libDirectory; public void contextualize(Context context) throws ContextException { this.context = context; } public void configure(Configuration configuration) throws ConfigurationException { this.showContinuations = configuration.getChild("show-continuations").getValueAsBoolean(true); this.showLibrary = configuration.getChild("show-libraries").getValueAsBoolean(true); } /** * Set the current <code>ServiceManager</code> instance used by this * <code>Serviceable</code>. * Need to get statistics about cache hits */ public void service(ServiceManager manager) throws ServiceException { super.service(manager); if (this.manager.hasService(StoreJanitor.ROLE)) { this.storeJanitor = (StoreJanitor) manager.lookup(StoreJanitor.ROLE); } else { getLogger().info("StoreJanitor is not available. Sorry, no cache statistics"); } if (this.manager.hasService(Store.PERSISTENT_STORE)) { this.storePersistent = (Store) this.manager.lookup(Store.PERSISTENT_STORE); } else { getLogger().info("Persistent Store is not available. Sorry no cache statistics about it."); } if(this.manager.hasService(ContinuationsManager.ROLE)) { continuationsManager = (ContinuationsManager) this.manager.lookup(ContinuationsManager.ROLE); } else { getLogger().info("ContinuationsManager is not available. Sorry no overview of created continuations"); } } public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException { super.setup(resolver, objectModel, src, par); if (this.showLibrary) { try { this.libDirectory = super.resolver.resolveURI("context://WEB-INF/lib"); } catch (SourceException e) { throw SourceUtil.handle(e); } } } /** * @see org.apache.avalon.framework.activity.Disposable#dispose() */ public void dispose() { if (this.manager != null) { this.manager.release(this.storePersistent); this.manager.release(this.storeJanitor); this.storePersistent = null; this.storeJanitor = null; } if (this.libDirectory != null) { super.resolver.release(this.libDirectory); this.libDirectory = null; } super.dispose(); } /** * Generate the status information in XML format. * @throws SAXException * when there is a problem creating the output SAX events. */ public void generate() throws SAXException, ProcessingException { // Start the document and set the namespace. super.contentHandler.startDocument(); super.contentHandler.startPrefixMapping("", NAMESPACE); super.contentHandler.startPrefixMapping(XLINK_PREFIX, XLINK_NS); genStatus(); // End the document. super.contentHandler.endPrefixMapping(XLINK_PREFIX); super.contentHandler.endPrefixMapping(""); super.contentHandler.endDocument(); } /** * Generate the main status document. */ private void genStatus() throws SAXException, ProcessingException { // Root element. // The current date and time. String dateTime = DateFormat.getDateTimeInstance().format(new Date()); String localHost; // The local host. try { localHost = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { getLogger().debug("StatusGenerator:UnknownHost", e); localHost = ""; } catch (SecurityException e) { getLogger().debug("StatusGenerator:Security", e); localHost = ""; } AttributesImpl atts = new AttributesImpl(); atts.addCDATAAttribute(NAMESPACE, "date", dateTime); atts.addCDATAAttribute(NAMESPACE, "host", localHost); atts.addCDATAAttribute(NAMESPACE, "cocoon-version", Constants.VERSION); super.contentHandler.startElement(NAMESPACE, "statusinfo", "statusinfo", atts); if (this.showContinuations) { genContinuationsTree(); } genVMStatus(); genProperties(); if (this.showLibrary) { genLibrarylist(); } // End root element. super.contentHandler.endElement(NAMESPACE, "statusinfo", "statusinfo"); } private void genContinuationsTree() throws SAXException { startGroup("Continuations"); Set continuations = this.continuationsManager.getForest(); for (Iterator i = continuations.iterator(); i.hasNext();) { displayContinuation(new WebContinuationDataBean((WebContinuation) i.next())); } endGroup(); } private void displayContinuation(WebContinuationDataBean wc) throws SAXException { AttributesImpl ai = new AttributesImpl(); ai.addAttribute(NAMESPACE, "id", "id", "CDATA", wc.getId()); ai.addAttribute(NAMESPACE, "interpreter", "interpreter", "CDATA", wc.getInterpreterId()); ai.addAttribute(NAMESPACE, "expire-time", "expire-time", "CDATA", wc.getExpireTime()); ai.addAttribute(NAMESPACE, "time-to-live", "time-to-live", "CDATA", wc.getTimeToLive() + "ms"); ai.addAttribute(NAMESPACE, "last-access-time", "last-access-time", "CDATA", wc.getLastAccessTime()); super.contentHandler.startElement(NAMESPACE, "cont", "cont", ai); List children = wc.get_children(); for (int i = 0; i < children.size(); i++) { displayContinuation((WebContinuationDataBean) children.get(i)); } super.contentHandler.endElement(NAMESPACE, "cont", "cont"); } private void genVMStatus() throws SAXException { AttributesImpl atts = new AttributesImpl(); startGroup("VM"); // BEGIN ClassPath String classpath = SystemUtils.JAVA_CLASS_PATH; if (classpath != null) { List paths = new ArrayList(); StringTokenizer tokenizer = new StringTokenizer(classpath, SystemUtils.PATH_SEPARATOR); while (tokenizer.hasMoreTokens()) { paths.add(tokenizer.nextToken()); } addMultilineValue("classpath", paths); } // END ClassPath // BEGIN CONTEXT CLASSPATH String contextClassPath = null; try { contextClassPath = (String) this.context.get(Constants.CONTEXT_CLASSPATH); } catch (ContextException e) { // we ignore this } if (contextClassPath != null) { List paths = new ArrayList(); StringTokenizer tokenizer = new StringTokenizer(contextClassPath, File.pathSeparator); while (tokenizer.hasMoreTokens()) { paths.add(tokenizer.nextToken()); } addMultilineValue("context-classpath", paths); } // END CONTEXT CLASSPATH // BEGIN Memory status startGroup("Memory"); final long totalMemory = Runtime.getRuntime().totalMemory(); final long freeMemory = Runtime.getRuntime().freeMemory(); addValue("total", String.valueOf(totalMemory)); addValue("used", String.valueOf(totalMemory - freeMemory)); addValue("free", String.valueOf(freeMemory)); endGroup(); // END Memory status // BEGIN JRE startGroup("JRE"); addValue("version", SystemUtils.JAVA_VERSION); atts.clear(); // qName = prefix + ':' + localName atts.addAttribute(XLINK_NS, "type", XLINK_PREFIX + ":type", "CDATA", "simple"); atts.addAttribute(XLINK_NS, "href", XLINK_PREFIX + ":href", "CDATA", SystemUtils.JAVA_VENDOR_URL); addValue("java-vendor", SystemUtils.JAVA_VENDOR, atts); endGroup(); // END JRE // BEGIN Operating system startGroup("Operating System"); addValue("name", SystemUtils.OS_NAME); addValue("architecture", SystemUtils.OS_ARCH); addValue("version", SystemUtils.OS_VERSION); endGroup(); // END operating system // BEGIN Cache if (this.storeJanitor != null) { startGroup("Store Janitor"); // For each element in StoreJanitor Iterator i = this.storeJanitor.iterator(); while (i.hasNext()) { Store store = (Store) i.next(); startGroup(store.getClass().getName() + " (hash = 0x" + Integer.toHexString(store.hashCode()) + ")"); int size = 0; int empty = 0; atts.clear(); atts.addAttribute(NAMESPACE, "name", "name", "CDATA", "cached"); super.contentHandler.startElement(NAMESPACE, "value", "value", atts); atts.clear(); Enumeration e = store.keys(); while (e.hasMoreElements()) { size++; Object key = e.nextElement(); Object val = store.get(key); String line; if (val == null) { empty++; } else { line = key + " (class: " + val.getClass().getName() + ")"; super.contentHandler.startElement(NAMESPACE, "line", "line", atts); super.contentHandler.characters(line.toCharArray(), 0, line.length()); super.contentHandler.endElement(NAMESPACE, "line", "line"); } } if (size == 0) { super.contentHandler.startElement(NAMESPACE, "line", "line", atts); String value = "[empty]"; super.contentHandler.characters(value.toCharArray(), 0, value.length()); super.contentHandler.endElement(NAMESPACE, "line", "line"); } super.contentHandler.endElement(NAMESPACE, "value", "value"); addValue("size", String.valueOf(size) + " items in cache (" + empty + " are empty)"); endGroup(); } endGroup(); } if (this.storePersistent != null) { startGroup(storePersistent.getClass().getName() + " (hash = 0x" + Integer.toHexString(storePersistent.hashCode()) + ")"); int size = 0; int empty = 0; atts.clear(); atts.addAttribute(NAMESPACE, "name", "name", "CDATA", "cached"); super.contentHandler.startElement(NAMESPACE, "value", "value", atts); atts.clear(); Enumeration e = this.storePersistent.keys(); while (e.hasMoreElements()) { size++; Object key = e.nextElement(); Object val = storePersistent.get(key); String line; if (val == null) { empty++; } else { line = key + " (class: " + val.getClass().getName() + ")"; super.contentHandler.startElement(NAMESPACE, "line", "line", atts); super.contentHandler.characters(line.toCharArray(), 0, line.length()); super.contentHandler.endElement(NAMESPACE, "line", "line"); } } if (size == 0) { super.contentHandler.startElement(NAMESPACE, "line", "line", atts); String value = "[empty]"; super.contentHandler.characters(value.toCharArray(), 0, value.length()); super.contentHandler.endElement(NAMESPACE, "line", "line"); } super.contentHandler.endElement(NAMESPACE, "value", "value"); addValue("size", size + " items in cache (" + empty + " are empty)"); endGroup(); } // END Cache endGroup(); } private void genProperties() throws SAXException { this.startGroup("System-Properties"); final Properties p = System.getProperties(); final Enumeration e = p.keys(); while ( e.hasMoreElements() ) { final String key = (String)e.nextElement(); final String value = p.getProperty(key); this.addValue(key, value); } this.endGroup(); } private void genLibrarylist() throws SAXException,ProcessingException { try { if (this.libDirectory instanceof TraversableSource) { startGroup("WEB-INF/lib"); Set files = new TreeSet(); Collection kids = ((TraversableSource) this.libDirectory).getChildren(); try { for (Iterator i = kids.iterator(); i.hasNext(); ) { final Source lib = (Source) i.next(); final String name = lib.getURI().substring(lib.getURI().lastIndexOf('/') + 1); files.add(name); } } finally { for (Iterator i = kids.iterator(); i.hasNext(); ) { final Source lib = (Source) i.next(); super.resolver.release(lib); } } for (Iterator i = files.iterator(); i.hasNext(); ) { addValue("file", (String) i.next()); } endGroup(); } } catch (SourceException e) { throw new ResourceNotFoundException("Could not read directory", e); } } /** Utility function to begin a <code>group</code> tag pair. */ private void startGroup(String name) throws SAXException { startGroup(name, null); } /** Utility function to begin a <code>group</code> tag pair with added attributes. */ private void startGroup(String name, Attributes atts) throws SAXException { AttributesImpl ai = (atts == null) ? new AttributesImpl() : new AttributesImpl(atts); ai.addAttribute(NAMESPACE, "name", "name", "CDATA", name); super.contentHandler.startElement(NAMESPACE, "group", "group", ai); } /** Utility function to end a <code>group</code> tag pair. */ private void endGroup() throws SAXException { super.contentHandler.endElement(NAMESPACE, "group", "group"); } /** Utility function to begin and end a <code>value</code> tag pair. */ private void addValue(String name, String value) throws SAXException { addValue(name, value, null); } /** Utility function to begin and end a <code>value</code> tag pair with added attributes. */ private void addValue(String name, String value, Attributes atts) throws SAXException { AttributesImpl ai = (atts == null) ? new AttributesImpl() : new AttributesImpl(atts); ai.addAttribute(NAMESPACE, "name", "name", "CDATA", name); super.contentHandler.startElement(NAMESPACE, "value", "value", ai); super.contentHandler.startElement(NAMESPACE, "line", "line", XMLUtils.EMPTY_ATTRIBUTES); if (value != null) { super.contentHandler.characters(value.toCharArray(), 0, value.length()); } super.contentHandler.endElement(NAMESPACE, "line", "line"); super.contentHandler.endElement(NAMESPACE, "value", "value"); } /** Utility function to begin and end a <code>value</code> tag pair. */ private void addMultilineValue(String name, List values) throws SAXException { addMultilineValue(name, values, null); } /** Utility function to begin and end a <code>value</code> tag pair with added attributes. */ private void addMultilineValue(String name, List values, Attributes atts) throws SAXException { AttributesImpl ai = (atts == null) ? new AttributesImpl() : new AttributesImpl(atts); ai.addAttribute(NAMESPACE, "name", "name", "CDATA", name); super.contentHandler.startElement(NAMESPACE, "value", "value", ai); for (int i = 0; i < values.size(); i++) { String value = (String) values.get(i); if (value != null) { super.contentHandler.startElement(NAMESPACE, "line", "line", XMLUtils.EMPTY_ATTRIBUTES); super.contentHandler.characters(value.toCharArray(), 0, value.length()); super.contentHandler.endElement(NAMESPACE, "line", "line"); } } super.contentHandler.endElement(NAMESPACE, "value", "value"); } }