blob: f92971deb12c7c2d98e70cc056e45e84e48f14c6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002-2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
*******************************************************************************/
package org.eclipse.wst.wsi.internal.core.log.impl;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import org.eclipse.wst.wsi.internal.core.ToolEnvironment;
import org.eclipse.wst.wsi.internal.core.ToolInfo;
import org.eclipse.wst.wsi.internal.core.WSIConstants;
import org.eclipse.wst.wsi.internal.core.WSIException;
import org.eclipse.wst.wsi.internal.core.log.LogReader;
import org.eclipse.wst.wsi.internal.core.log.MessageEntry;
import org.eclipse.wst.wsi.internal.core.log.MessageEntryHandler;
import org.eclipse.wst.wsi.internal.core.log.MimePart;
import org.eclipse.wst.wsi.internal.core.log.MimeParts;
import org.eclipse.wst.wsi.internal.core.monitor.config.Comment;
import org.eclipse.wst.wsi.internal.core.monitor.config.impl.CommentImpl;
import org.eclipse.wst.wsi.internal.core.profile.validator.EntryContext;
import org.eclipse.wst.wsi.internal.core.report.ArtifactReference;
import org.eclipse.wst.wsi.internal.core.report.Entry;
import org.eclipse.wst.wsi.internal.core.report.impl.ArtifactReferenceImpl;
import org.eclipse.wst.wsi.internal.core.report.impl.EntryImpl;
import org.eclipse.wst.wsi.internal.core.util.EntryType;
import org.eclipse.wst.wsi.internal.core.util.Utils;
import org.eclipse.wst.wsi.internal.core.xml.XMLUtils;
import org.eclipse.wst.wsi.internal.core.xml.dom.ElementLocation;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
/**
* Defines the implementation used to read the Log file.
*
* @version 1.0.1
* @author Neil Delima (nddelima@ca.ibm.com)
* @author Peter Brittenham (peterbr@us.ibm.com)
* @author Graham Turrell (gturrell@uk.ibm.com)
*/
public class LogReaderImpl implements LogReader
{
/**
* Document location.
*/
private String documentURI;
/** Used to ensure that the implementer is the first element after the log element **/
private int firstLogElement = 0;
private int elementCount = 0;
private boolean bMonitorFirst = false;
private WSIException wsiException = null;
/**
* @see org.eclipse.wst.wsi.internal.core.log.LogReader#readLog(String, LogEntryHandler)
*/
public void readLog(String logURI, MessageEntryHandler logReaderCallback)
throws WSIException
{
// Read the log file entries
readLog(new InputSource(logURI), logReaderCallback);
}
/**
* @see org.eclipse.wst.wsi.internal.core.log.LogReader#readLog(Reader, LogEntryHandler)
*/
public void readLog(Reader reader, MessageEntryHandler logReaderCallback)
throws WSIException
{
// Read the log file entries
readLog(new InputSource(reader), logReaderCallback);
}
/**
* Read and parse the log file.
* @param inputSource an InputSource object
* @param messageEntryHandler a MessageEntryHandler object
* @throws WSIException if a problem occurs while reading the log file.
*/
protected void readLog(
InputSource inputSource,
MessageEntryHandler messageEntryHandler)
throws WSIException
{
try
{
// Get the XML reader
XMLReader xmlReader = XMLUtils.getXMLReader();
/** TO DO: The LogFile must be valid wrt its schema. The schemaLocation probably
* needs to be set in some properties file.
* These two are to turn on validation
reader.setFeature(FEATURE_VALIDATION,true);
*/
// Set the content handler
xmlReader.setContentHandler(new LogFileHandler(messageEntryHandler));
// Start parsing the file
xmlReader.parse(inputSource);
// ADD: Need to find a way to determine when this exception should be thrown,
// since by the time we get here all of the test assertions have been processed.
// Also, only the last exception is propogated back. Should all of the exceptions
// be captured and propogated back?
if (wsiException != null)
{
// TEMP: For now, don't throw the exception
//throw wsiException;
}
//Requirement, the monitor MUST be the first child of the log.
//Perhaps we need to preprocess the log if this is the first condition to be met.
if (!bMonitorFirst)
throw new WSIException(
"The Element \""
+ WSIConstants.ELEM_MONITOR
+ "\" is not the first child of the Element \"log\"");
}
catch (Exception e)
{
throw new WSIException(
"An error occurred while processing the message log file.",
e);
}
}
/**
* @see org.eclipse.wst.wsi.internal.core.document.DocumentReader#getLocation()
*/
public String getLocation()
{
return this.documentURI;
}
/**
* @see org.eclipse.wst.wsi.internal.core.document.DocumentReader#setLocation(String)
*/
public void setLocation(String documentURI)
{
this.documentURI = documentURI;
}
/**
* Process and handles elements and attribute in the log file and invokes
* message validation assertions serially as the file is being processed,
* if a messagevalidator exists.
*/
private class LogFileHandler extends DefaultHandler
{
/** private members that represent a MessageEntry.*/
private String id;
private String conversationID;
private String type;
private String timestamp;
private StringBuffer senderHostAndPort = null;
private StringBuffer receiverHostAndPort = null;
private StringBuffer messageContent = null;
private StringBuffer httpHeaders = null;
private StringBuffer boundary = null;
private StringBuffer mimeHeaders = null;
private StringBuffer mimeContent = null;
private MimeParts mimeParts = null;
private List boundaries = null;
private int bom = 0;
private String logTimestamp = null;
private StringBuffer monitorComment;
private boolean ignoreComment = false;
/** Other private members */
private String currentElement;
private ElementLocation entryElementLocation = null;
private ToolInfo toolInfo = null;
private Vector messageEntryList = new Vector();
private MessageEntryHandler logEntryHandler = null;
private Locator locator = null;
/**
* Log file handler.
*/
public LogFileHandler(MessageEntryHandler logEntryHandler)
{
this.logEntryHandler = logEntryHandler;
toolInfo = new ToolInfo();
toolInfo.setToolEnvironment(new ToolEnvironment());
}
/**
* Start document.
*/
public void startDocument()
{
}
/**
* Set document locato.
*/
public void setDocumentLocator(Locator locator)
{
this.locator = locator;
// DEBUG:
//System.out.println("line: " + locator.getLineNumber() + ", column: " + locator.getColumnNumber());
}
/**
* Start element.
*/
public void startElement(
String namespaceURI,
String localName,
String qName,
Attributes attributes)
{
// DEBUG:
//System.out.println("localName:" + localName + ", line: " + locator.getLineNumber() + ", column: " + locator.getColumnNumber());
//Requirement, the implementer MUST be the first child of the log
if (elementCount == 2)
if (firstLogElement == 1)
bMonitorFirst = true;
elementCount++;
// <log>
if (localName == WSIConstants.ELEM_LOG)
{
// Save the timestamp value
this.logTimestamp = attributes.getValue(WSIConstants.ATTR_TIMESTAMP);
}
// <wsi-config:configuration>
else if (
(localName == WSIConstants.ELEM_CONFIG)
&& (namespaceURI == WSIConstants.NS_URI_WSI_MONITOR_CONFIG))
{
// Save comment element if it exists
ignoreComment = false;
}
// <wsi-config:comment>
else if (
(localName == WSIConstants.ELEM_COMMENT)
&& (namespaceURI == WSIConstants.NS_URI_WSI_MONITOR_CONFIG))
{
// If this is the comment element within the configuration element, then get its content
if (!ignoreComment)
{
currentElement = WSIConstants.ELEM_COMMENT;
monitorComment = new StringBuffer();
}
}
// <wsi-config:redirect>
else if (
(localName == WSIConstants.ELEM_REDIRECT)
&& (namespaceURI == WSIConstants.NS_URI_WSI_MONITOR_CONFIG))
{
// After hitting the first redirect statement, ignore all comment elements
ignoreComment = true;
monitorComment = null;
}
// <monitor>
else if (localName == WSIConstants.ELEM_MONITOR)
{
firstLogElement++;
toolInfo.setName(WSIConstants.ELEM_MONITOR);
// Get the monitor version and release date
toolInfo.setVersion(attributes.getValue(WSIConstants.ATTR_VERSION));
toolInfo.setReleaseDate(
attributes.getValue(WSIConstants.ATTR_RELEASE_DATE));
}
// <implementer>
else if (localName == WSIConstants.ELEM_IMPLEMENTER)
{
currentElement = WSIConstants.ELEM_IMPLEMENTER;
toolInfo.setImplementer(attributes.getValue(WSIConstants.ATTR_NAME));
toolInfo.setLocation(attributes.getValue(WSIConstants.ATTR_LOCATION));
}
// <runtime>
else if (localName == WSIConstants.ELEM_RUNTIME)
{
currentElement = WSIConstants.ELEM_RUNTIME;
toolInfo.getToolEnvironment().setRuntimeName(
attributes.getValue(WSIConstants.ATTR_NAME));
toolInfo.getToolEnvironment().setRuntimeVersion(
attributes.getValue(WSIConstants.ATTR_VERSION));
}
// <operatingSystem>
else if (localName == WSIConstants.ELEM_OPERATING_SYSTEM)
{
currentElement = WSIConstants.ELEM_OPERATING_SYSTEM;
toolInfo.getToolEnvironment().setOSName(
attributes.getValue(WSIConstants.ATTR_NAME));
toolInfo.getToolEnvironment().setOSVersion(
attributes.getValue(WSIConstants.ATTR_VERSION));
}
// <xmlParser>
else if (localName == WSIConstants.ELEM_XML_PARSER)
{
currentElement = WSIConstants.ELEM_XML_PARSER;
toolInfo.getToolEnvironment().setXMLParserName(
attributes.getValue(WSIConstants.ATTR_NAME));
toolInfo.getToolEnvironment().setXMLParserVersion(
attributes.getValue(WSIConstants.ATTR_VERSION));
}
// <messageEntry>
else if (localName == WSIConstants.ELEM_MESSAGE_ENTRY)
{
currentElement = WSIConstants.ELEM_MESSAGE_ENTRY;
entryElementLocation = new ElementLocation(this.locator);
timestamp = attributes.getValue(WSIConstants.ATTR_TIMESTAMP);
conversationID = attributes.getValue(WSIConstants.ATTR_CONVERSATION_ID);
id = attributes.getValue(WSIConstants.ATTR_ID_UC);
type = attributes.getValue(WSIConstants.ATTR_TYPE);
}
// <httpHeaders>
else if (localName == WSIConstants.ELEM_HTTP_HEADERS)
{
//Note: Character data could be split up and returned
//in more more than one characters call.
httpHeaders = new StringBuffer();
currentElement = WSIConstants.ELEM_HTTP_HEADERS;
}
// <messageContent>
else if (localName == WSIConstants.ELEM_MESSAGE_CONTENT)
{
messageContent = new StringBuffer();
mimeParts = null;
currentElement = WSIConstants.ELEM_MESSAGE_CONTENT;
String bomString = attributes.getValue(WSIConstants.ATTR_BOM);
if (bomString != null)
bom = Integer.valueOf(bomString).intValue();
else
bom = 0;
}
// <messageContentWithAttachments>
else if (localName == WSIConstants.ELEM_MESSAGE_CONTENT_WITH_ATTACHMENTS)
{
messageContent = null;
mimeParts = new MimePartsImpl();
String bomString = attributes.getValue(WSIConstants.ATTR_BOM);
if (bomString != null)
bom = Integer.valueOf(bomString).intValue();
else
bom = 0;
}
// <mimePart>
else if (localName == WSIConstants.ELEM_MIME_PART)
{
if (boundaries == null)
boundaries = new ArrayList(2);
else
boundaries.clear();
}
// <boundaryString>
else if (localName == WSIConstants.ELEM_BOUNDARY_STRING)
{
boundary = new StringBuffer();
currentElement = WSIConstants.ELEM_BOUNDARY_STRING;
}
// <mimeHeaders>
else if (localName == WSIConstants.ELEM_MIME_HEADERS)
{
mimeHeaders = new StringBuffer();
currentElement = WSIConstants.ELEM_MIME_HEADERS;
}
// <mimeContent>
else if (localName == WSIConstants.ELEM_MIME_CONTENT)
{
mimeContent = new StringBuffer();
currentElement = WSIConstants.ELEM_MIME_CONTENT;
}
// <senderHostAndPort>
else if (localName == WSIConstants.ELEM_SENDER_HOST_AND_PORT)
{
senderHostAndPort = new StringBuffer();
currentElement = WSIConstants.ELEM_SENDER_HOST_AND_PORT;
}
// <receiverHostAndPort>
else if (localName == WSIConstants.ELEM_RECEIVER_HOST_AND_PORT)
{
receiverHostAndPort = new StringBuffer();
currentElement = WSIConstants.ELEM_RECEIVER_HOST_AND_PORT;
}
}
/**
* Characters callback.
*/
public void characters(char ch[], int start, int length)
{
// Ignoring CRLF that appears in log elements
int altStart = start;
int altLength = length;
if (start > 0 && ch[start-1] == 13 && ch[start] == 10)
{
altStart++;
altLength--;
}
// <wsi-monConfig:comment>
if (monitorComment != null && currentElement == WSIConstants.ELEM_COMMENT)
{
monitorComment.append(ch, start, length);
}
// <messageContent>
else if (messageContent != null
&& currentElement == WSIConstants.ELEM_MESSAGE_CONTENT)
{
messageContent.append(ch, start, length);
}
// <httpHeaders>
else if (httpHeaders != null
&& currentElement == WSIConstants.ELEM_HTTP_HEADERS)
{
httpHeaders.append(ch, start, length);
}
// <boundaryString>
else if (boundary != null
&& currentElement == WSIConstants.ELEM_BOUNDARY_STRING)
{
boundary.append(ch, altStart, altLength);
}
// <mimeHeaders>
else if (mimeHeaders != null
&& currentElement == WSIConstants.ELEM_MIME_HEADERS)
{
mimeHeaders.append(ch, altStart, altLength);
}
// <mimeContent>
else if (mimeContent != null
&& currentElement == WSIConstants.ELEM_MIME_CONTENT)
{
mimeContent.append(ch, altStart, altLength);
}
// <senderHostAndPort>
else if (senderHostAndPort != null
&& currentElement == WSIConstants.ELEM_SENDER_HOST_AND_PORT)
{
senderHostAndPort.append(ch, start, length);
}
// <receiverHostAndPort>
else if (receiverHostAndPort != null
&& currentElement == WSIConstants.ELEM_RECEIVER_HOST_AND_PORT)
{
receiverHostAndPort.append(ch, start, length);
}
}
/**
* End Element.
*/
public void endElement(String namespaceURI, String localName, String qName)
{
// <wsi-config:comment>
if (
(localName == WSIConstants.ELEM_COMMENT)
&& (namespaceURI == WSIConstants.NS_URI_WSI_MONITOR_CONFIG))
{
// If we saved the comment, then process it and the log timestamp
if (this.monitorComment != null)
{
ArtifactReference artifactReference = new ArtifactReferenceImpl();
artifactReference.setTimestamp(this.logTimestamp);
Comment comment = new CommentImpl();
comment.setText(monitorComment.toString());
artifactReference.setDocumentElement(
comment,
WSIConstants.NS_NAME_WSI_MONITOR_CONFIG);
processArtifactReference(artifactReference);
}
}
// <boundaryString>
else if (localName == WSIConstants.ELEM_BOUNDARY_STRING)
{
// Adding boundary string to a list
boundaries.add(boundary.toString());
}
// <mimePart>
else if (localName == WSIConstants.ELEM_MIME_PART)
{
// Creating a MessageMIMEPart instance
MimePart part = new MimePartImpl();
part.setHeaders(mimeHeaders.toString());
part.setContent(mimeContent.toString());
part.setBoundaryStrings((String[])boundaries.toArray(new String[0]));
mimeParts.addPart(part);
}
// <messageEntry>
else if (localName == WSIConstants.ELEM_MESSAGE_ENTRY)
{
// Assumption: Related messages are sequentially available in the message log.
if (MessageEntry.TYPE_REQUEST.equalsIgnoreCase(type))
{
// Create log entry
MessageEntry messageEntryRequest =
createMessageEntry(
id,
conversationID,
type,
timestamp,
senderHostAndPort.toString(),
receiverHostAndPort.toString(),
messageContent == null ? "" : messageContent.toString(),
httpHeaders.toString(),
mimeParts,
entryElementLocation);
// Add log entry to the list
messageEntryList.add(messageEntryRequest);
// Request is now processed only when the response is received.
}
else if (MessageEntry.TYPE_RESPONSE.equalsIgnoreCase(type))
{
// Create log entry
MessageEntry messageEntryResponse =
createMessageEntry(
id,
conversationID,
type,
timestamp,
senderHostAndPort.toString(),
receiverHostAndPort.toString(),
messageContent == null ? "" : messageContent.toString(),
httpHeaders.toString(),
mimeParts,
entryElementLocation);
// Add log entry to the list
messageEntryList.add(messageEntryResponse);
if (logEntryHandler != null)
{
// look up the request message
// ISSUE : need to throw & catch a nullpointerexception in here...
MessageEntry messageEntryRequest = findRelatedRequest(messageEntryResponse);
if (messageEntryRequest != null)
{
// Create entry
// ADD: Need to create entry from report
//Entry entry = this.reporter.getReport().createEntry();
Entry entry = new EntryImpl();
entry.setEntryType(
EntryType.getEntryType(EntryType.TYPE_MESSAGE_REQUEST));
entry.setReferenceID(messageEntryRequest.getId());
entry.setEntryDetail(messageEntryRequest);
// Create the context for the request-response pair
EntryContext requestTargetContext =
new EntryContext(
entry,
messageEntryRequest,
messageEntryResponse);
if (requestTargetContext != null)
processLogEntry(requestTargetContext);
}
// Create entry
// ADD: Need to create entry from report
//Entry entry = this.reporter.getReport().createEntry();
Entry entry = new EntryImpl();
entry.setEntryType(
EntryType.getEntryType(EntryType.TYPE_MESSAGE_RESPONSE));
entry.setReferenceID(messageEntryResponse.getId());
entry.setEntryDetail(messageEntryResponse);
EntryContext responseTargetContext =
new EntryContext(
entry,
messageEntryRequest,
messageEntryResponse);
if (responseTargetContext != null)
processLogEntry(responseTargetContext);
}
}
}
currentElement = null;
}
/**
* End Document.
*/
public void endDocument()
{
//If we want to serially process the log file. Invoke validateMessages with
//msgValidator!=null. Here we have the option of clearing the logEntry vector
//once message validation is done.
//if (msgValidator!=null)
//logEntryList.clear();
}
/**
* Process the message in the log entry.
*/
public void processLogEntry(EntryContext entryContext)
{
try
{
//Before validating any messages check if implementer is the first element.
logEntryHandler.processLogEntry(entryContext);
}
catch (Exception e)
{
wsiException =
new WSIException(
"An error occurred while validating"
+ " the message logEntry: "
+ entryContext.getMessageEntry().getId(),
e);
}
}
/**
* Process the artifact reference.
*/
private void processArtifactReference(ArtifactReference artifactReference)
{
try
{
//Before validating any messages check if implementer is the first element.
logEntryHandler.processArtifactReference(artifactReference);
}
catch (Exception e)
{
wsiException =
new WSIException(
"An error occurred while processing"
+ " the artifact reference: "
+ artifactReference.toString(),
e);
}
}
/**
* Find the related Request LogEntry. The current rule for related log entires is
* that a request will always precede its response.
* ISSUE: Sequence in log file is timestamp driven (monitor spec).
* Assertion is that for a given conversationID, a second request is not sent
* until its response is received (chronologically).
* This method will have to be modified if the rule changes.
*/
// could be made more efficient by keeping a much smaller list of unresponded requests...
public MessageEntry findRelatedRequest(MessageEntry logEntryResponse)
{
for (int entry = messageEntryList.size() - 1; entry >= 0; entry--)
{
// Get the log entry of the matching request
MessageEntry logEntry = (MessageEntry) messageEntryList.get(entry);
// Ignore own entry
if (!logEntryResponse.equals(logEntry))
{
if (logEntryResponse
.getConversationId()
.equals(logEntry.getConversationId()))
{
// found the most recently read message with the same conversationID.
// From above, this should be the corresponding request. Check as far as possible.
if (logEntry.getType().equals(MessageEntry.TYPE_REQUEST))
{
return logEntry;
}
else
{
return null; // expected a request. need to throw an exception!
}
}
}
}
return null;
// conversationID match not found anywhere. need to throw an exception!
}
/**
* Create log entry.
*/
private MessageEntry createMessageEntry(
String id,
String conversationId,
String type,
String timestamp,
String senderIPPort,
String receiverIPPort,
String messageContent,
String httpHeaders,
MimeParts mimeParts,
ElementLocation elementLocation)
{
// Create log entry
MessageEntry messageEntry = new MessageEntryImpl();
messageEntry.setId(id);
messageEntry.setConversationId(conversationID);
messageEntry.setType(type);
messageEntry.setTimestamp(timestamp);
messageEntry.setSenderHostAndPort(senderIPPort);
messageEntry.setReceiverHostAndPort(receiverIPPort);
messageEntry.setHTTPHeaders(httpHeaders);
messageEntry.setBOM(bom);
messageEntry.setElementLocation(elementLocation);
String encoding = null;
if (mimeParts == null)
{
// Get the encoding for this message content
encoding = Utils.getXMLEncoding(messageContent);
messageEntry.setMessage(messageContent);
messageEntry.setMimeContent(false);
}
else
{
messageEntry.setMimeParts(mimeParts);
MimePart root = Utils.findRootPart(httpHeaders, mimeParts.getParts());
if (root != null)
{
// Get the encoding for this message content
encoding = Utils.getXMLEncoding(root.getContent());
mimeParts.setRootPart(root);
}
// else empty body
messageEntry.setMimeContent(true);
}
if ((encoding == null) || (encoding.equals("")))
encoding = WSIConstants.DEFAULT_XML_ENCODING;
messageEntry.setEncoding(encoding);
return messageEntry;
}
} //End ContentHandler
}