package de.ugoe.cs.eventbench; import java.io.IOException; import java.security.InvalidParameterException; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.NoSuchElementException; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; import org.jdom.input.SAXBuilder; import de.ugoe.cs.eventbench.data.Event; import de.ugoe.cs.eventbench.data.ReplayableEvent; import de.ugoe.cs.eventbench.data.WindowTree; import de.ugoe.cs.eventbench.data.WindowTreeNode; import de.ugoe.cs.eventbench.data.WindowsMessage; import de.ugoe.cs.util.console.Console; /** *

* Translates sequences of windows messages into events that can be used by the * Logalyzer core libraries for usage analysis. *

* * @author Steffen Herbold * */ public class EventGenerator { /** *

* Helper method that fetches the document node of an XML file. *

* * @param filename * name of the XML file * @return the document node */ private static Document getDocument(String filename) { SAXBuilder builder = new SAXBuilder(); Document doc = null; try { doc = builder.build(filename); rulesNamespace = Namespace.getNamespace("ul:rules"); } catch (JDOMException e) { System.err.println("Invalid rules file."); e.printStackTrace(); } catch (IOException e) { System.err.println("Invalid rules file."); e.printStackTrace(); } return doc; } /** *

* Name and path of the XML files containing the rules. *

*/ private String rulesFile; /** *

* Iterator used for the current sequence. *

*/ private ListIterator sequenceIterator; /** *

* Token that is currently being generated. *

*/ private ReplayableEvent currentToken; /** *

* Reference to the ul:rules namespace. *

*/ private static Namespace rulesNamespace; /** *

* The name of the rule that is currently being evaluated. *

*/ private String currentRuleName; /** *

* Internal message storage. Used to implement the * {@literal } and {@literal } * tags. *

*/ private Map messageStorage; /** *

* Creates a new EventGenerator. Sets "rules/rules.xml" as default file for * the rules. *

*/ public EventGenerator() { rulesFile = "rules/rules.xml"; } /** *

* Tries to match the rules to the given sequence to generate an * {@link Event}. *

*

* The rules are matched the order, in which they are defined in the XML * file. Therefore, the order of the rules in the file defines priorities, * when multiple rules could be matched to the same sequence. *

* * @param sequence * sequence of message for which an event will be generated * @return event that matches the messages; null, if no rule can be matched */ @SuppressWarnings("unchecked") public Event generateEvent(List sequence) { Document rulesDoc = getDocument(rulesFile); Element rulesRoot = rulesDoc.getRootElement(); List ruleElements = rulesRoot.getChildren("rule", rulesNamespace); boolean isMatch = false; for (int ruleIndex = 0; ruleIndex < ruleElements.size() && !isMatch; ruleIndex++) { Element currentRule = ruleElements.get(ruleIndex); currentToken = new ReplayableEvent(currentRuleName); currentRuleName = currentRule.getAttributeValue("name"); isMatch = true; messageStorage = new HashMap(); sequenceIterator = sequence.listIterator(); List ruleChildrenMsg = currentRule.getChildren("msg", rulesNamespace); int i = 0; while (isMatch && i < ruleChildrenMsg.size()) { Element messageElement = ruleChildrenMsg.get(i); if ("true".equals(messageElement.getAttributeValue("multiple"))) { Element nextMessageElement = null; if (i + 1 < ruleChildrenMsg.size()) { nextMessageElement = ruleChildrenMsg.get(i + 1); } try { isMatch = matchMultipleMessages(messageElement, nextMessageElement); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); } } else { try { isMatch = matchSingleMessage(messageElement); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); } } i++; } if (isMatch) { List ruleChildren = currentRule.getChildren(); for (Element genMsgElement : ruleChildren) { if (genMsgElement.getName().equals("genMsg")) { try { generateReplayMessage(genMsgElement); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); currentToken.invalidateReplay(); } } else if (genMsgElement.getName().equals("genMsgSeq")) { try { generateReplaySequence(genMsgElement); currentToken.invalidateReplay(); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); currentToken.invalidateReplay(); } } } Element idinfoElement = currentRule.getChild("idinfo", rulesNamespace); if (idinfoElement != null) { // cannot be empty if document is valid List valueElements = idinfoElement.getChildren(); currentToken.setIdInfo(getTermValue(null, valueElements.get(0))); } Console.traceln(currentRule.getAttributeValue("name") + currentToken.getIdInfo() + " matched"); } else { currentToken = null; } } if (!isMatch) { Console.traceln("no match found for sequence: " + sequence.toString()); } return currentToken; } private boolean createSequenceLParam( List generatedMessageSeq, boolean msgsGenerated, int constMsgType, Element termElement) throws NoSuchElementException { Iterator seqIterator = generatedMessageSeq.iterator(); if (termElement.getName().equals("seqValue")) { String obj = termElement.getAttributeValue("seqObj"); List seqVar = getStoredSeqVariable(obj); if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) { throw new InvalidParameterException( "Failure generating replay sequence for rule " + currentRuleName + ": One or more of the sequence variables used to generate a sequence have different lenghts."); } for (WindowsMessage msg : seqVar) { WindowsMessage currentSeqMsg = getCurrentSeqMsg( generatedMessageSeq, msgsGenerated, constMsgType, seqIterator); String paramValueStr = msg.getParameter(termElement .getAttributeValue("param")); int paramValue = 0; try { paramValue = Integer.parseInt(paramValueStr); currentSeqMsg.setLPARAM(paramValue); } catch (NumberFormatException e) { currentSeqMsg.setLPARAMasWindowDesc(paramValueStr); } } if (seqIterator.hasNext()) { // the first seq-var has a different number of elements than the // current one throw new NoSuchElementException(); } msgsGenerated = true; } else { // const value int paramValue = Integer.parseInt(getTermValue(null, termElement)); while (seqIterator.hasNext()) { seqIterator.next().setLPARAM(paramValue); } } return msgsGenerated; } private boolean createSequenceTarget( List generatedMessageSeq, boolean msgsGenerated, int constMsgType, Element termElement) throws NoSuchElementException { Iterator seqIterator = generatedMessageSeq.iterator(); if (termElement.getName().equals("seqValue")) { String obj = termElement.getAttributeValue("seqObj"); List seqVar = getStoredSeqVariable(obj); if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) { throw new InvalidParameterException( "Failure generating replay sequence for rule " + currentRuleName + ": One or more of the sequence variables used to generate a sequence have different lenghts."); } for (WindowsMessage msg : seqVar) { WindowsMessage currentSeqMsg = getCurrentSeqMsg( generatedMessageSeq, msgsGenerated, constMsgType, seqIterator); String targetString = msg.getParameter(termElement .getAttributeValue("param")); currentSeqMsg.setXmlWindowDescription(targetString); } msgsGenerated = true; } else { // const value throw new AssertionError("target must be a sequence variable!"); /* * If target would not be a variable, the message-elements could not * yet be created and the whole sequence might be broken. If this is * to be changed, createSequenceLParam and createSequenceWParam need * to be addepted, too. */ } return msgsGenerated; } private boolean createSequenceWParam( List generatedMessageSeq, boolean msgsGenerated, int constMsgType, Element termElement) throws NoSuchElementException { Iterator seqIterator = generatedMessageSeq.iterator(); if (termElement.getName().equals("seqValue")) { String obj = termElement.getAttributeValue("seqObj"); List seqVar = getStoredSeqVariable(obj); if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) { throw new InvalidParameterException( "Failure generating replay sequence for rule " + currentRuleName + ": One or more of the sequence variables used to generate a sequence have different lenghts."); } for (WindowsMessage msg : seqVar) { WindowsMessage currentSeqMsg = getCurrentSeqMsg( generatedMessageSeq, msgsGenerated, constMsgType, seqIterator); String paramValueStr = msg.getParameter(termElement .getAttributeValue("param")); int paramValue = 0; try { paramValue = Integer.parseInt(paramValueStr); currentSeqMsg.setWPARAM(paramValue); } catch (NumberFormatException e) { currentSeqMsg.setWPARAMasWindowDesc(paramValueStr); } } if (seqIterator.hasNext()) { // the first seq-var has a different number of elements than the // current one throw new NoSuchElementException(); } msgsGenerated = true; } else { // const value int paramValue = Integer.parseInt(getTermValue(null, termElement)); while (seqIterator.hasNext()) { seqIterator.next().setWPARAM(paramValue); } } return msgsGenerated; } @SuppressWarnings("unchecked") private boolean evalEqualRestrictions(WindowsMessage currentMessage, Element messageElement) { boolean isMatch = true; for (Element childElement : (List) messageElement.getChildren( "equals", rulesNamespace)) { List termElements = childElement.getChildren(); // the size 2 of termElements is guaranteed by the XML schema String value1 = getTermValue(currentMessage, termElements.get(0)); String value2 = getTermValue(currentMessage, termElements.get(1)); if (value1 == null || value2 == null) { isMatch = false; } else { isMatch = isMatch && value1.equals(value2); } } for (Element childElement : (List) messageElement.getChildren( "equalsSeq", rulesNamespace)) { List termElements = childElement.getChildren(); List values1 = getTermValueSeq(currentMessage, termElements.get(0)); List values2 = getTermValueSeq(currentMessage, termElements.get(0)); if (values1 == null || values2 == null) { isMatch = false; } else { isMatch = isMatch && values1.equals(values2); } } return isMatch; } @SuppressWarnings("unchecked") private void generateReplayMessage(Element genMsgElement) { List genMsgChildren = genMsgElement.getChildren(); WindowsMessage generatedMessage = null; if (genMsgChildren.size() == 1) { // replay stored message without // change String obj = genMsgChildren.get(0).getAttributeValue("obj"); generatedMessage = getStoredMessageVariable(null, obj); } else { // generate message according to the rule for (Element genMsgChild : genMsgChildren) { Element termElement = (Element) genMsgChild.getChildren() .get(0); if (genMsgChild.getName().equals("type")) { try { int msgType = Integer.parseInt(getTermValue(null, termElement)); generatedMessage = new WindowsMessage(msgType); } catch (NumberFormatException e) { throw new InvalidParameterException( "Failure generating replay sequence for rule " + currentRuleName + ": Defined type is not an integer."); } } else if (genMsgChild.getName().equals("target")) { String targetString = getTermValue(null, termElement); generatedMessage.setXmlWindowDescription(targetString); } else if (genMsgChild.getName().equals("LPARAM")) { String paramValueStr = getTermValue(null, termElement); int paramValue = 0; try { paramValue = Integer.parseInt(paramValueStr); generatedMessage.setLPARAM(paramValue); } catch (NumberFormatException e) { generatedMessage.setLPARAMasWindowDesc(paramValueStr); } } else if (genMsgChild.getName().equals("WPARAM")) { String paramValueStr = getTermValue(null, termElement); int paramValue = 0; try { paramValue = Integer.parseInt(paramValueStr); generatedMessage.setWPARAM(paramValue); } catch (NumberFormatException e) { generatedMessage.setWPARAMasWindowDesc(paramValueStr); } } } } if (generatedMessage != null) { int delay = Integer.parseInt(genMsgElement .getAttributeValue("delay")); generatedMessage.setDelay(delay); } else { currentToken.invalidateReplay(); } currentToken.addReplayEvent(generatedMessage); } @SuppressWarnings("unchecked") private void generateReplaySequence(Element genMsgElement) { List genMsgSeqChildren = genMsgElement.getChildren(); List generatedMessageSeq = new LinkedList(); if (genMsgSeqChildren.size() == 1) { String obj = genMsgSeqChildren.get(0).getAttributeValue("seqObj"); generatedMessageSeq = getStoredSeqVariable(obj); } else { boolean msgsGenerated = false; int constMsgType = 0; for (Element genMsgSeqChild : genMsgSeqChildren) { Element termElement = (Element) genMsgSeqChild.getChildren() .get(0); if (genMsgSeqChild.getName().equals("type")) { // note: cannot easily be extracted because of mulitple // return values if (termElement.getName().equals("seqValue")) { String obj = termElement.getAttributeValue("seqObj"); List seqVar = getStoredSeqVariable(obj); for (WindowsMessage msg : seqVar) { generatedMessageSeq.add(new WindowsMessage(msg .getType())); } msgsGenerated = true; } else { // constValue type constMsgType = Integer.parseInt(getTermValue(null, termElement)); } } else if (genMsgSeqChild.getName().equals("target")) { msgsGenerated = createSequenceTarget(generatedMessageSeq, msgsGenerated, constMsgType, termElement); } else if (genMsgSeqChild.getName().equals("LPARAM")) { msgsGenerated = createSequenceLParam(generatedMessageSeq, msgsGenerated, constMsgType, termElement); } else if (genMsgSeqChild.getName().equals("WPARAM")) { msgsGenerated = createSequenceWParam(generatedMessageSeq, msgsGenerated, constMsgType, termElement); } } } currentToken.addReplaySequence(generatedMessageSeq); } private WindowsMessage getCurrentSeqMsg( List generatedMessageSeq, boolean msgsGenerated, int constMsgType, Iterator seqIterator) { WindowsMessage currentSeqMsg = null; if (msgsGenerated) { currentSeqMsg = seqIterator.next(); } else { currentSeqMsg = new WindowsMessage(constMsgType); generatedMessageSeq.add(currentSeqMsg); } return currentSeqMsg; } private WindowsMessage getStoredMessageVariable( WindowsMessage currentMessage, String obj) throws InvalidParameterException { WindowsMessage varMessage = null; if (obj.equals("this")) { if (currentMessage == null) { throw new InvalidParameterException( "Failure obtaining term value for rule " + currentRuleName + ": \"this\" is not a valid name for generating runtime messages."); } varMessage = currentMessage; } else { Object tmp = messageStorage.get(obj); if (tmp instanceof WindowsMessage) { varMessage = (WindowsMessage) tmp; } else { throw new InvalidParameterException( "Failure obtaining term value for rule " + currentRuleName + ": No message \"" + obj + "\" stored."); } } return varMessage; } @SuppressWarnings("unchecked") private List getStoredSeqVariable(String obj) throws InvalidParameterException { List varMsgSeq = null; Object tmp = messageStorage.get(obj); if (tmp instanceof List) { varMsgSeq = (List) tmp; } else { throw new InvalidParameterException( "Failure obtaining term value for rule " + currentRuleName + ": No sequence \"" + obj + "\" store."); } return varMsgSeq; } private String getTermValue(WindowsMessage currentMessage, Element termElement) { String value = null; WindowsMessage varMessage = null; if (termElement.getName().equals("constValue")) { value = termElement.getAttributeValue("value"); } else if (termElement.getName().equals("paramValue")) { String objectName = termElement.getAttributeValue("obj"); varMessage = getStoredMessageVariable(currentMessage, objectName); if (varMessage != null) { String param = termElement.getAttributeValue("param"); value = varMessage.getParameter(param); } } else if (termElement.getName().equals("winInfoValue")) { String objectName = termElement.getAttributeValue("obj"); varMessage = getStoredMessageVariable(currentMessage, objectName); if (varMessage != null) { String paramString = termElement.getAttributeValue("winParam"); if (paramString.equals("class")) { value = varMessage.getWindowClass(); } else if (paramString.equals("resourceId")) { value = "" + varMessage.getWindowResourceId(); } else if (paramString.equals("hwnd")) { value = "" + varMessage.getHwnd(); } } } else if (termElement.getName().equals("msgInfoValue")) { String objectName = termElement.getAttributeValue("obj"); varMessage = getStoredMessageVariable(currentMessage, objectName); if (varMessage != null) { String paramString = termElement.getAttributeValue("msgParam"); if (paramString.equals("type")) { value = "" + varMessage.getType(); } else if (paramString.equals("target")) { value = varMessage.getXmlWindowDescription(); } } } return value; } private List getTermValueSeq(WindowsMessage currentMessage, Element termElement) { List values = new LinkedList(); if (termElement.getName().equals("seqValue")) { String obj = termElement.getAttributeValue("seqObj"); String param = termElement.getAttributeValue("param"); List seqVar = getStoredSeqVariable(obj); for (WindowsMessage msg : seqVar) { // msg.getParameter returns null, if parameter is not found, // therefore the List can contain null-values values.add(msg.getParameter(param)); } } return values; } @SuppressWarnings("unchecked") private void handleStorage(Element messageElement, WindowsMessage currentMessage) { for (Element childElement : (List) messageElement.getChildren( "store", rulesNamespace)) { String identifier = childElement.getAttributeValue("var"); messageStorage.put(identifier, currentMessage); resolveHwnd(currentMessage, childElement); } for (Element childElement : (List) messageElement.getChildren( "storeSeq", rulesNamespace)) { String identifier = childElement.getAttributeValue("varSeq"); Object tmp = messageStorage.get(identifier); List storedSequence; if (tmp == null || tmp instanceof WindowsMessage) { storedSequence = new LinkedList(); storedSequence.add(currentMessage); messageStorage.put(identifier, storedSequence); } else if (tmp instanceof List) { storedSequence = (List) tmp; storedSequence.add(currentMessage); messageStorage.put(identifier, storedSequence); } resolveHwnd(currentMessage, childElement); } } private boolean matchMultipleMessages(Element messageElement, Element nextMessageElement) { boolean isMatch = false; boolean isCurrentMatch = false; boolean nextMatchFound = false; WindowsMessage currentMessage = null; WindowsMessage nextMessage = null; int type = Integer.parseInt(messageElement.getAttributeValue("type")); int nextType = -1; if (nextMessageElement != null) { nextType = Integer.parseInt(nextMessageElement .getAttributeValue("type")); } while (!nextMatchFound && sequenceIterator.hasNext()) { currentMessage = sequenceIterator.next(); if (type == currentMessage.getType()) { isCurrentMatch = evalEqualRestrictions(currentMessage, messageElement); isMatch = isMatch || isCurrentMatch; if (isCurrentMatch) { handleStorage(messageElement, currentMessage); currentToken.setTarget(currentMessage .getXmlWindowDescription()); currentToken .setTargetShort(currentMessage.getParentNames()); } } if (nextMessageElement != null && isMatch) { // peek next message to check if the sequence ends and the next // match is found if (!sequenceIterator.hasNext()) { return false; // sequence is over, but not all messages are // found } nextMessage = sequenceIterator.next(); sequenceIterator.previous(); if (nextType == nextMessage.getType()) { nextMatchFound = evalEqualRestrictions(nextMessage, nextMessageElement); } } } return isMatch; } private boolean matchSingleMessage(Element messageElement) { boolean isMatch = false; WindowsMessage currentMessage = null; int type = Integer.parseInt(messageElement.getAttributeValue("type")); while (!isMatch && sequenceIterator.hasNext()) { // traverses the messages from the current position forward till a // message with the correct type is found currentMessage = sequenceIterator.next(); if (type == currentMessage.getType()) { // message with the correct type found // eval child nodes for further matching/storing isMatch = evalEqualRestrictions(currentMessage, messageElement); // in case the message is a match, eval storage children if (isMatch) { handleStorage(messageElement, currentMessage); currentToken.setTarget(currentMessage .getXmlWindowDescription()); currentToken .setTargetShort(currentMessage.getParentNames()); } } } return isMatch; } @SuppressWarnings("unchecked") private void resolveHwnd(WindowsMessage currentMessage, Element childElement) { List resolveElements = childElement.getChildren("resolveHwnd", rulesNamespace); for (Element resolveElement : resolveElements) { String param = resolveElement.getAttributeValue("param"); String storeParam = resolveElement.getAttributeValue("storeParam"); int paramHwnd = Integer .parseInt(currentMessage.getParameter(param)); WindowTreeNode node = WindowTree.getInstance().find(paramHwnd); if (node != null) { currentMessage.addParameter(storeParam, node.xmlRepresentation()); } } } }