/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2002 * Copyright by ESO (in the framework of the ALMA collaboration) * and Cosylab 2002, All rights reserved * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package com.cosylab.logging.engine.log; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random; import java.util.Vector; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import com.cosylab.logging.engine.DataNode; import com.cosylab.logging.engine.VectorNodeList; import alma.acs.util.IsoDateFormat; /** * This is the container class for generic Log Entries. * Attributes, messages and Data(s) are obtained via public * variables. Properties, used for management of LogEntries * can be obtained via accessor and mutator methods. This * class is used when parsing nodes. The class is used when * a file is read or a node is received by the push consumer. * <p> * Note on reuse of equal Strings, Integers etc: * The XML parser currently produces separate instances of the same Strings, * so that a lot of memory would be used up unnecessarily. * This class reuses String objects for its field values, * but does not exchange the String instances that are referenced by the <code>log Node</code>. * That current design will only yield a memory advantage if the <code>LogEntryXML</code> * instance is not kept permanently in the application, but instead its fields are read * and the values transfered (without the DOM Node) to an object of another class. * Ale: please check if there are other Srings that could be reused based on <code>stringPool</code>, * and if we should use also a pool for Integers, at least for log levels since they are always the same few. */ public final class LogEntryXML implements ILogEntry { private VectorNodeList datas = null; public VectorNodeList complexLogEntryMessage = null; private final Object[] fields = new Object[LogField.values().length]; private Node log = null; private boolean isLogEntrySimple = true; // The simple date format used to write and read dates from a string private SimpleDateFormat dateFormat = new IsoDateFormat(); public LogEntryXML(String stackId, int stackLevel) throws DOMException { setField(LogField.STACKID, stackId); setField(LogField.STACKLEVEL, Integer.valueOf(stackLevel)); } /** * This costructor is used only for testing purposes. * It is called by com.cosylab.logging.engine.simulator.simulatorRemoteAccess * It generates a random LogEntryXML */ // public LogEntryXML(Random random) { public LogEntryXML(Random random) { // set whatever you want here (depending of the test you are performing); setField(LogField.TIMESTAMP, System.currentTimeMillis()); setField(LogField.ENTRYTYPE, LogTypeHelper.values()[((short) random.nextInt(LogTypeHelper.values().length))]); if (random.nextInt(10) < 3) { setField(LogField.LINE, Integer.valueOf(random.nextInt(100))); } if (random.nextInt(2) == 0) setField(LogField.STACKID, "Terminator"); else setField(LogField.STACKID, "Exterminator"); } /** * This constructor is called by the DOMParser. * @param log org.w3c.Node */ public LogEntryXML(Node log) throws DOMException { initialize(log); } public static LogEntryXML generateRandomLog(Random random) { return new LogEntryXML(random); } /** * Check if the log entry has datas * * @return ture datas is not null */ public boolean hasDatas() { return datas!=null; } /** * Data Nodes are returned as a org.w3c.dom.NodeList. * If data(s) are not present, null is returned. */ public NodeList getDatas() { return datas; } public Node getNode() { return log; } /** * Returns the type of this Object as String. * "Undeclared" id type is not specified. * @return String */ public String getEntryTypeAsString() { return LogTypeHelper.getLogTypeDescription(this); } /** * Returns a specific field according to field name constants. * Creation date: (11/21/2001 18:16:03) * @return java.lang.Object * @param fieldIndex int index of the field to return */ public Object getField(LogField f) { return fields[f.ordinal()]; } /** * @see ILogEntry */ public LogTypeHelper getType() { return LogTypeHelper.values()[(Integer)getField(LogField.ENTRYTYPE)]; } private void initAttributes(Node log) throws DOMException { //public void initAttributes(Node log) throws DOMException { NamedNodeMap nnm = log.getAttributes(); Node attr; attr = nnm.getNamedItem(LogField.TIMESTAMP.getTagAttribute()); if (attr == null) throw new DOMException( DOMException.NOT_FOUND_ERR, "TimeStamp attribute is missing in " + getField(LogField.ENTRYTYPE)); // The time stamp is required! try { setField(LogField.TIMESTAMP, dateFormat.parse(attr.getNodeValue()).getTime()); } catch (ParseException pe) { throw new DOMException(DOMException.SYNTAX_ERR, "Error while parsing TimeStamp: " + pe); } // Search for the others fields (they are not required so nothing will // happen is some of them are missing) attr = nnm.getNamedItem(LogField.FILE.getTagAttribute()); if (attr != null) setField(LogField.FILE, attr.getNodeValue()); attr = nnm.getNamedItem(LogField.LINE.getTagAttribute()); if (attr != null) setField(LogField.LINE, new Integer(attr.getNodeValue())); attr = nnm.getNamedItem(LogField.ROUTINE.getTagAttribute()); if (attr != null) setField(LogField.ROUTINE, attr.getNodeValue()); attr = nnm.getNamedItem(LogField.HOST.getTagAttribute()); if (attr != null) setField(LogField.HOST, attr.getNodeValue()); attr = nnm.getNamedItem(LogField.PROCESS.getTagAttribute()); if (attr != null) setField(LogField.PROCESS, attr.getNodeValue()); attr = nnm.getNamedItem(LogField.CONTEXT.getTagAttribute()); if (attr != null) setField(LogField.CONTEXT, attr.getNodeValue()); attr = nnm.getNamedItem(LogField.THREAD.getTagAttribute()); if (attr != null) setField(LogField.THREAD, attr.getNodeValue()); attr = nnm.getNamedItem(LogField.STACKID.getTagAttribute()); if (attr != null) setField(LogField.STACKID, attr.getNodeValue()); attr = nnm.getNamedItem(LogField.STACKLEVEL.getTagAttribute()); if (attr != null) setField(LogField.STACKLEVEL, new Integer(attr.getNodeValue())); attr = nnm.getNamedItem(LogField.LOGID.getTagAttribute()); if (attr != null) setField(LogField.LOGID, attr.getNodeValue()); attr = nnm.getNamedItem(LogField.PRIORITY.getTagAttribute()); if (attr != null) setField(LogField.PRIORITY, new Integer(attr.getNodeValue())); attr = nnm.getNamedItem(LogField.URI.getTagAttribute()); if (attr != null) setField(LogField.URI, attr.getNodeValue()); attr = nnm.getNamedItem(LogField.SOURCEOBJECT.getTagAttribute()); if (attr != null) { setField(LogField.SOURCEOBJECT, attr.getNodeValue()); } attr = nnm.getNamedItem(LogField.AUDIENCE.getTagAttribute()); if (attr != null) { setField(LogField.AUDIENCE, attr.getNodeValue()); } attr = nnm.getNamedItem(LogField.ARRAY.getTagAttribute()); if (attr != null) { setField(LogField.ARRAY, attr.getNodeValue()); } attr = nnm.getNamedItem(LogField.ANTENNA.getTagAttribute()); if (attr != null) { setField(LogField.ANTENNA, attr.getNodeValue()); } } private void initBody(Node log) throws DOMException { NodeList list = log.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { Node node = list.item(i); boolean isData = node.getNodeName().equals("Data"); short type = node.getNodeType(); if (isData) { DataNode dataNode = new DataNode(node); if (datas == null) { datas = new VectorNodeList(1, 2); } datas.add(dataNode); } else { if (isLogEntrySimple) { isLogEntrySimple = !node.hasChildNodes(); } if (isLogEntrySimple) { // simpleLogEntryMessage = node.getNodeValue(); setField(LogField.LOGMESSAGE, node.getNodeValue()); } else { if (complexLogEntryMessage == null) complexLogEntryMessage = new VectorNodeList(1, 2); complexLogEntryMessage.add(node); } } } } private void initialize(Node log) throws DOMException { initLogEntryType(log); initAttributes(log); initBody(log); this.log = log; } private void initLogEntryType(Node log) throws DOMException { String logEntryType = log.getNodeName(); if (logEntryType == null) throw new DOMException(DOMException.SYNTAX_ERR, "logEntryType is null."); // todo: reuse Integer objects setField(LogField.ENTRYTYPE,LogTypeHelper.fromLogTypeDescription(logEntryType).ordinal()); if (getField(LogField.ENTRYTYPE) == null) throw new DOMException(DOMException.NOT_FOUND_ERR, "Unknown logEntryType: " + logEntryType); } /** * This boolean tag specifies whether LogEntryMessage is simple or not: * <UL><LI><code>logEntryMessageSimple = true</code>: Log message is a single <code>String</code>. It is obtained via * <code> String logEntry.simpleLogEntryMessage</code>. Value is null if message is not present.</LI> * <LI><code>logEntryMessageSimple = false</code>: Log message is more complex, it is an XML <code> * org.w3c.dom.NodeList</code>. It is obtained via <code>org.w3c.dom.NodeList logEntry.complexLogEntryMessage</code>. * Value is null if message is simple.</LI> * </UL> */ public boolean isLogEntryMessageSimple() { return false; } /** * Insert the method's description here. * Creation date: (12/4/2001 12:21:35) * @return boolean * @param index int */ public final static boolean isValidLogEntryType(int index) { return ((index >= 0) && (index < 9)); } /** * Sets the specified field. This method is protected since the fields are not * to be modified. The only time this is called is during initialization. * Creation date: (11/21/2001 18:35:10) * @param field The field * @param value java.lang.Object value to set */ protected void setField(LogField field, Object value) { fields[field.ordinal()] = value; } /** * Returns a String representation of the log. */ public String toString() { StringBuffer sb = new StringBuffer("--- LogEntryXML ---\n"); /* Attributes */ for (LogField f: LogField.values()) { if (f==LogField.ENTRYTYPE) { sb.append(f.getName()+": "+ getEntryTypeAsString() + "\n"); } else { sb.append(f.getName()+": " + getField(f) + "\n"); } } /* Data(s) */ if (datas != null) sb.append("Datas: " + datas + "\n"); return sb.toString(); } /** * Recursively prints the struct of the node * * @param logNode The node to print */ public static void printNode(Node logNode,int depth) { String indent=""; for (int i=0; i<depth; i++) indent=indent+"\t"; System.out.print(indent+"Node name "+logNode.getNodeName()); System.out.print(", type "+logNode.getNodeType()); System.out.println(", val "+logNode.getNodeValue()); NamedNodeMap map = logNode.getAttributes(); if (map!=null) { for (int i=0; i<map.getLength(); i++) { Node nd =map.item(i); System.out.print(indent+"\tAttr name "+nd.getNodeName()); System.out.print(", type "+nd.getNodeType()); System.out.println(", val "+nd.getNodeValue()); } } NodeList childs = logNode.getChildNodes(); if (childs!=null) { for (int i=0; i<childs.getLength(); i++) { printNode(childs.item(i),depth+1); } } } /** * Return the object as XML string */ public String toXMLString() { if (log==null) { throw new IllegalStateException("Node is null"); } StringBuffer sb = new StringBuffer(); String logType =getEntryTypeAsString(); sb.append("<"+logType); for (LogField f: LogField.values()) { if (f==LogField.LOGMESSAGE || f==LogField.ENTRYTYPE) { continue; } Object attrValue = getField(f); if (attrValue!=null) { if (f==LogField.TIMESTAMP) { Date dt = new Date((Long)attrValue); StringBuffer dateSB = new StringBuffer(); java.text.FieldPosition pos = new java.text.FieldPosition(0); dateFormat.format(dt,dateSB,pos); attrValue=dateSB.toString(); } String attrValStr = attrValue.toString(); attrValStr=attrValStr.replaceAll("<","<"); attrValStr=attrValStr.replaceAll(">",">"); sb.append(" "+f.getTagAttribute()+"=\""+attrValStr+"\""); } } LogTypeHelper type = getType(); if (type==LogTypeHelper.TRACE && getField(LogField.LOGMESSAGE)!=null && getField(LogField.LOGMESSAGE).toString().trim().isEmpty()) { sb.append("/>"); } else { sb.append("><![CDATA["+getField(LogField.LOGMESSAGE).toString()+"]]>"); sb.append(getXMLDatas()); sb.append("</"+logType+">"); } return sb.toString(); } private StringBuffer getXMLDatas() { StringBuffer tempStr = new StringBuffer(); if (datas!=null) { Vector<AdditionalData> temp=getAdditionalData(); if (temp==null) { // No additional data return tempStr; } for (int t=0; t<temp.size(); t++) { tempStr.append("<Data Name=\""+temp.get(t).name+"\"><![CDATA["); tempStr.append(temp.get(t).value+"]]></Data>"); } } return tempStr; } /** * The vector return contains only strings and it is formed * in this way: * name value name value name value... * i.e. it is a plain representation of couples of values * * @return a Vector of String with the key and value of each * additional data * If the log does not contain any additional data, * returns null */ public Vector<AdditionalData> getAdditionalData() { Vector<AdditionalData> tempVector = null; if (datas!=null) { if (datas.size()==0) { return null; } tempVector = new Vector<AdditionalData>(); int size = datas.size(); for (int t=0; t<size; t++) { String dataName=null; Node dataItem = datas.item(t); if (dataItem.hasAttributes()) { org.w3c.dom.NamedNodeMap attr=dataItem.getAttributes(); for (int at=0; at<attr.getLength(); at++) { org.w3c.dom.Node attrNode = attr.item(t); if (attrNode!=null) { dataName = attrNode.getTextContent().trim(); // Cleanup dataName=dataName.replaceAll("<","<"); dataName=dataName.replaceAll(">",">"); } else { org.w3c.dom.Node temp = attr.getNamedItem("Name"); if (temp!=null) { dataName=temp.getNodeValue().replaceAll("<","<").replaceAll(">",">").trim(); } else { // The name should always be defined! dataName=("N/A"); } } } } String dataContent = ""; if (dataItem.hasChildNodes()) { Node child = dataItem.getFirstChild(); dataContent= child.getNodeValue().trim().replaceAll("<","<").replaceAll(">",">"); } tempVector.add(new AdditionalData(dataName,dataContent)); } } return tempVector; } /** * Add a data node to this log: * <Data Name=name>value</Data> * * @param name The name, i.e. the key of the pair * @param value The value of the field */ public void addData(String name, String value) { Document doc = log.getOwnerDocument(); Element ele = doc.createElement("Data"); ele.setAttribute("Name",name); Text txt = doc.createTextNode(value); ele.appendChild(txt); if (datas==null) { datas = new VectorNodeList(1,1); } datas.add(ele); } }