// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed 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 de.ugoe.cs.autoquest.plugin.http; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.soap.MessageFactory; import javax.xml.soap.SOAPMessage; import javax.xml.transform.stream.StreamSource; import org.xml.sax.SAXException; import de.ugoe.cs.autoquest.eventcore.Event; import de.ugoe.cs.autoquest.eventcore.IEventType; import de.ugoe.cs.autoquest.plugin.http.eventcore.HTTPEventType; import de.ugoe.cs.autoquest.plugin.http.eventcore.HTTPTarget; import de.ugoe.cs.autoquest.plugin.http.eventcore.SOAPEventType; import de.ugoe.cs.autoquest.plugin.http.logdata.Content; import de.ugoe.cs.autoquest.plugin.http.logdata.HttpExchange; import de.ugoe.cs.autoquest.plugin.http.logdata.Session; import de.ugoe.cs.util.console.Console; /** *

* Parser for HTTP Monitor logs. Uses JAXB for parsing and is therefore quite simple. For each * exchange in the log, it creates an appropriate event. It differes between default HTTP events * and SOAP events. *

* * @author Patrick Harms */ public class HTTPLogParser { /** *

* the event sequences parsed by this parser *

*/ private Collection> sequences = new LinkedList>(); /** *

* the message factory used for parsing SOAP messages *

*/ private MessageFactory soapMessageFactory; /** *

* properties used to map the path of a service to its logical name *

*/ private Properties urlNameMap = null; /** *

* Constructor. Creates a new HTTPLogParser without a urlNameMap. *

*/ public HTTPLogParser() { Logger.getLogger("com.sun.xml.internal.messaging.saaj").setLevel(Level.OFF); } /** *

* Constructor. Creates a new HTTPLogParser with a urlNameMap *

* * @param urlNameMapFile properties file with the path to logical name map * @throws IOException thrown if there is an error load in the service name map */ public HTTPLogParser(File urlNameMapFile) throws IOException { this(new FileInputStream(urlNameMapFile)); } /** *

* Constructor. Creates a new HTTPLogParser with a urlNameMap *

* * @param urlNameMapFile properties file with the path to logical name map * @throws IOException thrown if there is an error load in the service name map */ public HTTPLogParser(InputStream urlNameMapStream) throws IOException { this(); urlNameMap = new Properties(); urlNameMap.load(urlNameMapStream); } /** *

* Constructor. Creates a new HTTPLogParser with a urlNameMap. *

* * @param urlNameMapFile properties that include the logical name map */ public HTTPLogParser(Properties urlNameMapFile) { this(); urlNameMap = urlNameMapFile; } /** *

* Parses a log file written by the HTTPMonitor and creates a collection of event sequences. *

* * @param filename * name and path of the log file * * @throws SAXException in the case, the file could not be parsed */ public void parseFile(String filename) throws JAXBException { if (filename == null) { throw new IllegalArgumentException("filename must not be null"); } parseFile(new File(filename)); } /** *

* Parses a log file written by the HTTPMonitor and creates a collection of event sequences. *

* * @param file * file to be parsed * * @throws JAXBException in the case, the file could not be parsed */ public void parseFile(File file) throws JAXBException { if (file == null) { throw new IllegalArgumentException("file must not be null"); } try { parseFile(new FileInputStream(file)); } catch (FileNotFoundException e) { Console.printerr("Error parsing file + " + file.getName()); Console.logException(e); return; } } /** *

* Parses a log file written by the HTTPMonitor and creates a collection of event sequences. *

* * @param stream * file to be parsed * * @throws JAXBException in the case, the file could not be parsed */ public void parseFile(InputStream stream) throws JAXBException { if (stream == null) { throw new IllegalArgumentException("stream must not be null"); } JAXBContext jc = JAXBContext.newInstance(Session.class.getPackage().getName()); Unmarshaller unmarshaller = jc.createUnmarshaller(); StreamSource source = new StreamSource(stream); @SuppressWarnings("unchecked") JAXBElement sessionObj = (JAXBElement) unmarshaller.unmarshal(source); Session session = sessionObj.getValue(); long orderingId = 0; if ((session.getHttpExchange() != null) && (session.getHttpExchange().size() > 0)) { List sequence = new LinkedList(); for (HttpExchange exchange : session.getHttpExchange()) { // check, if the exchange is an old one and does not contain ordering ids yet. // If not, then add them. if (exchange.getRequest().getOrderingId() == null) { exchange.getRequest().setOrderingId(orderingId++); } else { orderingId = exchange.getRequest().getOrderingId(); } if (exchange.getResponse().getOrderingId() == null) { exchange.getResponse().setOrderingId(orderingId++); } else { orderingId = exchange.getResponse().getOrderingId(); } // now handle the exchange sequence.add(new Event(createEvent(exchange), new HTTPTarget(exchange.getReceiver()))); } sequences.add(sequence); } } /** *

* instantiates the appropriate event type. If it encounters a SOAP exchange, a SOAP event type * is instantiated. Otherwise a normal HTTP event type is created. *

* * @param exchange the exchange for which an event type is to be created * * @return as described */ private IEventType createEvent(HttpExchange exchange) { Content requestContent = exchange.getRequest() != null ? exchange.getRequest().getContent() : null; SOAPMessage soapRequest = getSOAPMessage(requestContent); Content responseContent = exchange.getResponse() != null ? exchange.getResponse().getContent() : null; SOAPMessage soapResponse = getSOAPMessage(responseContent); if (soapRequest != null) { return new SOAPEventType(exchange, soapRequest, soapResponse, urlNameMap); } else { return new HTTPEventType(exchange); } } /** *

* convenience method to convert the content of an HTTP request or response into a SOAP message *

* * @param content the content to be converted into a SOAP message * * @return the SOAP message contained in the content or null if either the content is null or * the content does not contain a SOAP message */ private SOAPMessage getSOAPMessage(Content content) { if ((content != null) && (content.getData() != null)) { try { if (soapMessageFactory == null) { soapMessageFactory = MessageFactory.newInstance(); } String encoding = content.getEncoding(); if (encoding == null) { encoding = "UTF-8"; } InputStream in = new ByteArrayInputStream(content.getData().getBytes(encoding)); SOAPMessage message = soapMessageFactory.createMessage(null, in); // try to access something to see, if everything worked fine. message.getSOAPHeader(); message.getSOAPBody(); return message; } catch (Exception e) { if (content.getData().toLowerCase().indexOf("envelope") > 0) { Console.traceln(Level.WARNING, "HTTP message seems to be a SOAP message but " + "it could not be parsed as such: " + e); //Console.logException(e); } } } return null; } /** *

* returns the sequences parsed by this parser *

* * @return as described */ public Collection> getSequences() { return sequences; } }