All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bigdata.counters.XMLUtility Maven / Gradle / Ivy

/*

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     [email protected]

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
/*
 * Created on May 1, 2008
 */

package com.bigdata.counters;

import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.Iterator;
import java.util.regex.Pattern;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.bigdata.counters.History.SampleIterator;
import com.bigdata.counters.ICounterSet.IInstrumentFactory;
import com.bigdata.util.HTMLUtility;

/**
 * XML (de-)serialization of {@link CounterSet}s.
 * 
 * @author Bryan Thompson
 * @version $Id$
 */
public class XMLUtility {

    static protected final Logger log = Logger.getLogger(XMLUtility.class);

    public static final XMLUtility INSTANCE = new XMLUtility();

    private XMLUtility() {
    }

    /**
     * Serializes an {@link ICounterSet} as XML.
     * 
     * @param root
     *            The {@link ICounterSet}.
     * @param w
     *            Where to write the XML.
     * @param filter
     *            A filter to be applied to the counters (optional). Only the
     *            matched counters will be serialized.
     * 
     * @throws IOException
     */
    public void writeXML(CounterSet root, final Writer w, final Pattern filter)
            throws IOException {
        
        w.write("");
        
        final Iterator itr = root.postOrderIterator();

        while(itr.hasNext()) {
            
            final CounterSet counterSet = (CounterSet)itr.next();
            
            final Iterator itr2 = counterSet.counterIterator(filter);

            if(!itr2.hasNext()) {
                
                /*
                 * do not emit counter sets that do not have directly attached
                 * counters.
                 */
                
                continue;
                
            }
            
            w.write("");

            while(itr2.hasNext()) {
                
                final ICounter counter = itr2.next();
                
                final String name = counter.getName();
                
                final Object value;
                try {
                    value = counter.getValue();
                } catch (Throwable t) {
                    /*
                     * Log an error if we can't obtain the value of a counter
                     * and continue processing the remaining counters.
                     * 
                     * Note: Counter#getValue() typically invokes
                     * Instrument#sample() so an error here typically means that
                     * the instrument implementation class ran into a problem.
                     */
                    log.error("Could not read counter value (skipped): "
                            + counter.getPath(), t);
                    continue;
                }
                
                final long time = counter.lastModified();

                if (time == 0L || value == null) {
                    
                    /*
                     * Zero timestamps and null values are generally an
                     * indicator that the counter value is not yet defined.
                     */
                    
                    if (log.isInfoEnabled())
                        log.info("Ignoring counter: name=" + name
                                + ", timestamp=" + time + ", value=" + value);

                    continue;
                    
                }
                
                final String type = getXSDType(value);
            
                if (time < 0L) {
                    
                    /*
                     * Negative timestamps are not expected.
                     */
                    
                    log.warn("Ignoring counter with invalid timestamp: name="
                                    + name
                                    + ", timestamp="
                                    + time
                                    + ", value="
                                    + value);

                    continue;
                    
                }
                
                w.write("");
                
                if(counter.getInstrument() instanceof HistoryInstrument) {

                    final HistoryInstrument inst = (HistoryInstrument) counter
                            .getInstrument();

                    writeHistory(w, inst.getHistory(), "minutes");
                    
//                    writeHistory(w, inst.minutes, "minutes");
//                    
//                    writeHistory(w, inst.hours, "hours");
//                    
//                    writeHistory(w, inst.days, "days");
                       
                }
                
                w.write("");
                
            }

            w.write("");

        }
        
        w.write("");
        
        w.flush();
        
    }
    
    /**
     * Write the sample values for a {@link History} of some {@link ICounter}.
     * 
     * @param w
     * @param h
     * @param units
     * 
     * @throws IOException
     */
    protected void writeHistory(final Writer w, final History h,
            final String units) throws IOException {

        /*
         * Note: synchronized on the history to prevent concurrent modification.
         */
        synchronized(h) {
        
            w.write("");
            
            final SampleIterator itr = h.iterator();

            while (itr.hasNext()) {

                final IHistoryEntry entry = itr.next();
                
                w.write("");
                
            }
            
            w.write("");
        
        }

    }
    
    public void readXML(final CounterSet root, final InputStream is,
            final IInstrumentFactory instrumentFactory, final Pattern filter)
            throws IOException, ParserConfigurationException, SAXException {

        if (is == null)
            throw new IllegalArgumentException();

        if (instrumentFactory == null)
            throw new IllegalArgumentException();

        final SAXParser p;
        {

            final SAXParserFactory f = SAXParserFactory.newInstance();

            f.setNamespaceAware(true);

            p = f.newSAXParser();

        }

        final MyHandler handler = new MyHandler(root, instrumentFactory, filter);

        p.parse(is, handler /* @todo set validating and pass in systemId */);

    }
    
    /**
     * Helper class for SAX based parse of counter XML.
     * 
     * @author Bryan Thompson
     * @version $Id$
     */
    static private class MyHandler extends DefaultHandler {
        
        /** Note: inner class so named with '$' vs '.' */
        protected static final Logger log = Logger.getLogger(MyHandler.class);
        
        private final AbstractCounterSet root;
        
        private final IInstrumentFactory instrumentFactory;

        private final Pattern filter;
        
        public MyHandler(final AbstractCounterSet root,
                final IInstrumentFactory instrumentFactory, final Pattern filter) {

            if (root == null)
                throw new IllegalArgumentException();

            if (instrumentFactory == null)
                throw new IllegalArgumentException();

            this.root = root;
            
            this.instrumentFactory = instrumentFactory;
            
            this.filter = filter;
            
        }
        
        /**
         * Set each time we enter a cs element.
         */
        private String path;
        
        /** The current counter. */
        private ICounter counter;
        
        /**
         * The current history and null if we are not reading
         * some {@link History} for the current {@link #counter}.
         */
        private History history;
        
        /** qualified name for the cs element (counter set). */
        private final String cs = "cs"; 

        /** qualified name for the c element (counter). */
        private final String c = "c"; 
        
        /** qualified name for the h element (history). */
        private final String h = "h"; 

        /** qualified name for the h element (history value). */
        private final String v = "v"; 
        
        /** buffers the cdata content inside of each element. */
        private StringBuilder cdata = new StringBuilder();
        
        public void startElement(String uri, String localName, String qName,
                Attributes attributes) throws SAXException {

            if (log.isDebugEnabled())
                log.debug("uri=" + uri + ",localName=" + localName + ", qName="
                        + qName);

            if (qName.equals("counters")) {

                // ignore.

            } else if (qName.equals(cs)) {

                path = attributes.getValue("path");

                if (log.isInfoEnabled())
                    log.info("path=" + path);
                
            } else if (qName.equals(c)) {

                final String name = attributes.getValue("name");

                if (filter != null) {

                    final String fqn = path + ICounterSet.pathSeparator + name;

                    if (!filter.matcher(fqn).matches()) {

                        if (log.isInfoEnabled())
                            log.info("Does not match filter: " + fqn);

                        // will not process this counter.
                        counter = null;
                        
                        return;
                        
                    }

                }
                
                final String type = attributes.getValue("type");

                final long time = Long.parseLong(attributes.getValue("time"));

                final String value = attributes.getValue("value");

                if (log.isInfoEnabled())
                    log.info("path=" + path + ", name=" + name + ", type="
                            + type + ", value=" + value + ", time=" + time);

                // determine value class from XSD attribute.
                final Class typ = getType(type);

                // find/create counter given its path, etc.
                final ICounter counter = getCounter(path, name, typ);

                if (counter == null) {

                    log.warn("Conflict: path=" + path + ", name=" + name);

                } else {

                    // set the value on the counter.
                    setValue(counter, typ, value, time);
                    
                }

                /*
                 * Set in case we need to read the counter's history also.
                 * 
                 * Note: this will be [null] if we could not find/create the
                 * counter above.
                 */
                
                this.counter = counter;

                /*
                 * clear history reference - set when we see the [h] element and
                 * know the units for the history to be read.
                 */
                
                this.history = null;

            } else if (qName.equals(h)) {

                // clear - will be set below based on units and otherwise not available.
                history = null;

                if (counter == null) {

                    // The counter could not be read so ignore its history.
                    return;

                }

                if(!(counter.getInstrument() instanceof HistoryInstrument)) {
                    
                    /*
                     * Counter does not support history (either the factory is
                     * wrong or the counter pre-existed but was created without
                     * history support).
                     */
                    
                    log.warn("Ignoring history: inst="
                            + counter.getInstrument().getClass().getName()
                            + ", path" + counter);
                    
                    return;
                    
                }
                
                final HistoryInstrument inst = (HistoryInstrument) counter
                        .getInstrument();
                
                final String units = attributes.getValue("units");

                if (units == null) {

                    throw new SAXException("No units");
                    
                } else if (units.equals("minutes")) {

                    history = inst.minutes;
                    
                } else if (units.equals("hours")) {
                    
                    history = inst.hours;
                    
                } else if (units.equals("days")) {
                    
                    history = inst.days;
                    
                } else {
                    
                    throw new SAXException("Bad units: " + units);
                    
                }
                
            } else if(qName.equals(v)) {
            
                if (counter == null || history == null) {

                    // Ignore history.
                    return;
                    
                }
                
                final long time = Long.parseLong(attributes.getValue("time"));

                final String value = attributes.getValue("value");

                if (log.isInfoEnabled())
                    log.info("counter=" + counter + ", time=" + time
                            + ", value=" + value);

                addValue(history, time, value);

            } else {
                
                throw new SAXException("Unknown start tag: "+qName);
                
            }
            
        }

        public void characters(char[] ch, int start, int length)
                throws SAXException {

            cdata.append(ch, start, length);

        }
        
        public void endElement(String uri, String localName, String qName)
                throws SAXException {

            try {

//                    if (!qName.equals(c))
//                        return;

            } finally {

                // clear any buffered data.
                cdata.setLength(0);
                
            }
            
        }

        /**
         * Find/create a counter given its path, name, and value class.
         * 
         * @param path
         * @param name
         * @param typ
         * 
         * @return The counter -or- null iff the path and name
         *         identify a pre-existing {@link CounterSet}, which conflicts
         *         with the described {@link ICounter}.
         */
        protected ICounter getCounter(final String path, final String name,
                Class typ) {
            
            final ICounter counter;

            // iff there is an existing node for that path.
            final ICounterNode node;
            
            // atomic makePath + counter create iff necessary.
            synchronized (root) {

                /*
                 * Note: use just the name when the path is '/' to avoid
                 * forming a path that begins '//'.
                 */
                node = root.getPath(path.equals(ICounterSet.pathSeparator) ? name : path
                        + ICounterSet.pathSeparator + name);

                if (node == null) {

                    final IInstrument inst = instrumentFactory
                            .newInstance(typ);

                    counter = ((CounterSet)root.makePath(path)).addCounter(name, inst);

                } else if (node.isCounter()) {

                    counter = (ICounter) node;

                } else {

                    return null;

                }
                
            }

            return counter;
            
        }
        
        /**
         * Interpret an XSD attribute value, returning the corresponding Java class.
         * 
         * @param type
         *            The XSD attribute value.
         * 
         * @return
         */
        static protected Class getType(String type) {
            
            final String localType = type.substring(type.lastIndexOf("#")+1);
            
            final Class typ;
            
            if(localType.equals(xsd_int)||localType.equals(xsd_long)) {
                
                typ = Long.class;
                
            } else if(localType.equals(xsd_float)||localType.equals(xsd_double)) {
                
                typ = Double.class;
                
            } else {
                
                typ = String.class;
                
            }

            return typ;

        }
        
        /**
         * Set the counter value given its value type and the text of its value.
         * 
         * @param counter
         *            The counter whose value will be set.
         * @param typ
         *            The value type of the counter.
         * @param text
         *            The text of the value to be interpreted.
         * @param time
         *            The timestamp for the value.
         */
        static protected void setValue(final ICounter counter, final Class typ,
                final String text, final long time) {
            
            final IInstrument inst = counter.getInstrument();
            
            if (inst instanceof OneShotInstrument) {

                /*
                 * This instrument can not be updated. However, new values for a
                 * variety of one-shot counters will be reported by each client
                 * that starts on the same host. E.g., the #of CPUs and that
                 * sort of thing. We just ignore the redundent updates.
                 */
                
                log.warn(OneShotInstrument.class.getName()
                        + " : ignoring update: path=" + counter.getPath()
                        + ", value=" + text);
                
                return;
                
            }
            
            try {

                if (typ == Long.class) {

                    counter.setValue(Long.parseLong(text), time);

                } else if (typ == Double.class) {

                    counter.setValue(Double.parseDouble(text), time);

                } else {

                    counter.setValue(text, time);

                }
                
            } catch (Exception ex) {
                
                log.warn("Could not set counter value: path=" + counter.getPath()
                        + " : " + ex, ex);
                
            }

        }
        
        static protected void addValue(final History history, final long time,
                final String text) {

            final Class typ = history.getValueType();

            if (typ == Long.class) {

                history.add(time, Long.parseLong(text));

            } else if (typ == Double.class) {

                history.add(time, Double.parseDouble(text));

            } else {

                history.add(time, text);

            }

        }
        
    }

    private static final transient String NAMESPACE_XSD = "http://www.w3.org/2001/XMLSchema";
    
    /** assuming xs == http://www.w3.org/2001/XMLSchema */
    private static final transient String xsd = "xs:";
    private static final transient String xsd_anyType = xsd+"anyType";
    private static final transient String xsd_long    = xsd+"long";
    private static final transient String xsd_int     = xsd+"int";
    private static final transient String xsd_double  = xsd+"double";
    private static final transient String xsd_float   = xsd+"float";
    private static final transient String xsd_string  = xsd+"string";
    private static final transient String xsd_boolean = xsd+"boolean";

    /**
     * Return the XML datatype for an {@link ICounter}'s value.
     * 
     * @param value
     *            The current counter value.
     * 
     * @return The corresponding XML datatype -or- "xsd:anyType" if no more
     *         specific datatype could be determined.
     */
    private String getXSDType(Object value) {
        
        if (value == null)
            return xsd_anyType;

        Class c = value.getClass();
        
        if (c.equals(Long.class)) 
            
            return xsd_long;

        else if (c.equals(Integer.class))
            
            return xsd_int;
        
        else if (c.equals(Double.class))
        
            return xsd_double;
        
        else if (c.equals(Float.class))
            
            return xsd_float;
        
        else if (c.equals(String.class))
            
            return xsd_string;
        
        else if (c.equals(Boolean.class))
            
            return xsd_boolean;
        
        else
            
            return xsd_anyType;

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy