| /* |
| * 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"); |
| } |
| } |