source: trunk/autoquest-plugin-http/src/main/java/de/ugoe/cs/autoquest/plugin/http/SOAPUtils.java @ 1992

Last change on this file since 1992 was 1992, checked in by sherbold, 9 years ago
  • extended SOAPUtils with functions for sorting soap events and removing events that reference services that shall be ignored
  • Property svn:mime-type set to text/plain
File size: 20.3 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.plugin.http;
16
17import java.io.StringWriter;
18import java.util.ArrayList;
19import java.util.Collection;
20import java.util.HashSet;
21import java.util.Iterator;
22import java.util.LinkedList;
23import java.util.List;
24import java.util.Set;
25
26import javax.xml.transform.Transformer;
27import javax.xml.transform.TransformerException;
28import javax.xml.transform.TransformerFactory;
29import javax.xml.transform.TransformerFactoryConfigurationError;
30import javax.xml.transform.dom.DOMSource;
31import javax.xml.transform.stream.StreamResult;
32
33import org.w3c.dom.Attr;
34import org.w3c.dom.Element;
35import org.w3c.dom.Node;
36import org.w3c.dom.NodeList;
37
38import de.ugoe.cs.autoquest.eventcore.Event;
39import de.ugoe.cs.autoquest.plugin.http.eventcore.EqualSOAPDataMap;
40import de.ugoe.cs.autoquest.plugin.http.eventcore.SOAPEventType;
41import de.ugoe.cs.autoquest.plugin.http.eventcore.SimpleSOAPEventType;
42
43/**
44 * <p>
45 * Utilities for working with SOAP events. Their main task is to simply working with
46 * {@link SimpleSOAPEventType} and {@link SOAPEventType}.
47 * </p>
48 *
49 * @author Steffen Herbold
50 */
51public class SOAPUtils {
52
53    /**
54     * <p>
55     * Defines how sequences are ordered:
56     * <ul>
57     * <li>REQUEST: by the request order id</li>
58     * <li>RESPONSE: by the response order id</li>
59     * </ul>
60     * </p>
61     *
62     * @author Steffen Herbold
63     */
64    public enum SequenceOrder {
65        REQUEST, RESPONSE
66    }
67
68    /**
69     * <p>
70     * Replaces events with a {@link SOAPEventType} with new events of {@link SimpleSOAPEventType}
71     * and no target.
72     * </p>
73     *
74     * @param sequences
75     *            sequences where the events are replaced
76     * @param keepOnlySOAPEvents
77     *            if true, all events that are not of SOAPEventType or SimpleSOAPEventType are
78     *            removed
79     * @return sequences with {@link SimpleSOAPEventType}s instead of {@link SOAPEventType}s
80     */
81    public static Collection<List<Event>> convertToSimpleSOAPEvent(Collection<List<Event>> sequences,
82                                                                   boolean keepOnlySOAPEvents)
83    {
84        Collection<List<Event>> newSequences = new LinkedList<>();
85        EqualSOAPDataMap equalSOAPDataMap = new EqualSOAPDataMap();
86        for (List<Event> sequence : sequences) {
87            List<Event> newSequence = null;
88            if (sequence != null) {
89                newSequence = new LinkedList<>();
90                for (Event event : sequence) {
91                    if (event.getType() instanceof SOAPEventType) {
92                        SOAPEventType eventType = (SOAPEventType) event.getType();
93                        newSequence.add(new Event(new SimpleSOAPEventType(eventType
94                            .getCalledMethod(), eventType.getServiceName(), eventType
95                            .getClientName(), eventType.getSoapRequestBody(), equalSOAPDataMap)));
96                    }
97                    else {
98                        if (!keepOnlySOAPEvents || event.getType() instanceof SimpleSOAPEventType) {
99                            newSequence.add(event);
100                        }
101                    }
102                }
103            }
104            newSequences.add(newSequence);
105        }
106        return newSequences;
107    }
108
109    /**
110     * <p>
111     * Removes all non SOAP events from the contained sequences
112     * </p>
113     *
114     * @param sequences
115     *            sequences where the events are replaced
116     */
117    public static Collection<List<Event>> removeNonSOAPEvents(Collection<List<Event>> sequences) {
118        Collection<List<Event>> soapOnlySequences = new LinkedList<>();
119        for (List<Event> sequence : sequences) {
120            soapOnlySequences.add(removeNonSOAPEvents(sequence));
121        }
122        return soapOnlySequences;
123    }
124
125    /**
126     * <p>
127     * Removes all non SOAP events from a sequence
128     * </p>
129     *
130     * @param sequence
131     *            sequence where the events are replaced
132     */
133    public static List<Event> removeNonSOAPEvents(List<Event> sequence) {
134        List<Event> soapOnlySequence = new LinkedList<>();
135        for (Event event : sequence) {
136            if (event.getType() instanceof SOAPEventType) {
137                soapOnlySequence.add(event);
138            }
139        }
140        return soapOnlySequence;
141    }
142
143    /**
144     * <p>
145     * Helper function to get the name of a service from a SOAP event.
146     * </p>
147     *
148     * @param event
149     *            event for which the service name is retrieved
150     * @return service name
151     */
152    public static String getServiceNameFromEvent(Event event) {
153        if (event.getType() instanceof SOAPEventType) {
154            return ((SOAPEventType) event.getType()).getServiceName();
155        }
156        else if (event.getType() instanceof SimpleSOAPEventType) {
157            return ((SimpleSOAPEventType) event.getType()).getServiceName();
158        }
159        else {
160            throw new RuntimeException(
161                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
162                                           event.getType().getClass().getName());
163        }
164    }
165
166    /**
167     *
168     * <p>
169     * Helper function to get the called method from a SOAP event
170     * </p>
171     *
172     * @param event
173     *            event for which the called method is retrieved
174     * @return called method
175     */
176    public static String getCalledMethodFromEvent(Event event) {
177        if (event.getType() instanceof SOAPEventType) {
178            return ((SOAPEventType) event.getType()).getCalledMethod();
179        }
180        else if (event.getType() instanceof SimpleSOAPEventType) {
181            return ((SimpleSOAPEventType) event.getType()).getCalledMethod();
182        }
183        else {
184            throw new RuntimeException(
185                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
186                                           event.getType().getClass().getName());
187        }
188    }
189
190    /**
191     * <p>
192     * Helper function to get the name of a client from a SOAP event.
193     * </p>
194     *
195     * @param event
196     *            event for which the client name is retrieved
197     * @return service name
198     */
199    public static String getClientNameFromEvent(Event event) {
200        if (event.getType() instanceof SOAPEventType) {
201            return ((SOAPEventType) event.getType()).getClientName();
202        }
203        else if (event.getType() instanceof SimpleSOAPEventType) {
204            return ((SimpleSOAPEventType) event.getType()).getClientName();
205        }
206        else {
207            throw new RuntimeException(
208                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
209                                           event.getType().getClass().getName());
210        }
211    }
212
213    /**
214     * <p>
215     * Helper function to get the body of a SOAP request.
216     * </p>
217     *
218     * @param event
219     *            event for which the SOAP request body is retrieved
220     * @return body of the SOAP event
221     */
222    public static Element getSoapRequestBodyFromEvent(Event event) {
223        return getSoapRequestBodyFromEvent(event, false);
224    }
225
226    /**
227     * <p>
228     * Helper function to get the body of a SOAP request.
229     * </p>
230     *
231     * @param event
232     *            event for which the SOAP request body is retrieved
233     * @param useRandomRequestBodies
234     *            defines is random request bodies are used or the body of the associated event
235     * @return body of the SOAP event
236     */
237    public static Element getSoapRequestBodyFromEvent(Event event, boolean useRandomRequestBodies) {
238        Element requestBody = null;
239        if (event.getType() instanceof SOAPEventType) {
240            requestBody = ((SOAPEventType) event.getType()).getSoapRequestBody();
241        }
242        else if (event.getType() instanceof SimpleSOAPEventType) {
243            if (useRandomRequestBodies) {
244                requestBody = ((SimpleSOAPEventType) event.getType()).getRandomSoapRequestBody();
245            }
246            else {
247                requestBody = ((SimpleSOAPEventType) event.getType()).getSoapRequestBody();
248            }
249        }
250        return requestBody;
251    }
252
253    /**
254     * <p>
255     * returns the XML serialization of a DOM node; located here because it is used for SOAP request
256     * bodies
257     * </p>
258     *
259     * @param node
260     *            DOM node that is serialized
261     * @return XML serialization as String; null if node is null
262     */
263    public static String getSerialization(Node node) {
264        if (node == null) {
265            return null;
266        }
267        try {
268            StringWriter writer = new StringWriter();
269            Transformer transformer = TransformerFactory.newInstance().newTransformer();
270            transformer.transform(new DOMSource(node), new StreamResult(writer));
271            return writer.toString();
272        }
273        catch (TransformerFactoryConfigurationError | TransformerException e) {
274            throw new IllegalArgumentException(
275                                               "could not create serialization for SOAP request body.",
276                                               e);
277        }
278    }
279
280    /**
281     * <p>
282     * Fetches all {@link Element}s that are direct children of the parent node, whose name matches
283     * the given name.
284     * </p>
285     *
286     * @param typeNameRaw
287     *            name of elements that are looked for
288     * @param parentNode
289     *            DOM node in which the elements are searched for
290     * @return found {@link Element}s
291     */
292    public static List<Element> getMatchingChildNode(String typeNameRaw, Element parentNode) {
293        List<Element> matchingNodes = new ArrayList<>();
294        Node parameterNode = null;
295        if (parentNode != null) {
296            NodeList parameterNodes = parentNode.getChildNodes();
297            String[] typeNameSplit = typeNameRaw.split(":");
298            String typeName = typeNameSplit[typeNameSplit.length - 1];
299            for (int i = 0; i < parameterNodes.getLength(); i++) {
300                parameterNode = parameterNodes.item(i);
301                if (parameterNode.getNodeType() == Node.ELEMENT_NODE) {
302                    String[] parameterNodeSplit = parameterNode.getNodeName().split(":");
303                    String parameterNodeName = parameterNodeSplit[parameterNodeSplit.length - 1];
304                    if (typeName.equals(parameterNodeName)) {
305                        matchingNodes.add((Element) parameterNode);
306                    }
307                }
308            }
309        }
310        return matchingNodes;
311    }
312
313    /**
314     * <p>
315     * Returns the values found in a currentNode for a defined valueName. To this aim, this methods
316     * first determines if there is an attribute with the name "valueName". If this is the case, the
317     * value of this attribute is returned. Otherwise, the methods looks for {@link Element}s that
318     * are direct children of the provided DOM node with the given name and returns the text content
319     * of those nodes.
320     * </p>
321     * <p>
322     * In case no values can be found, an empty list is returned
323     *
324     * @param valueName
325     *            name of the value that is retrieved
326     * @param node
327     *            node for which the value is retrieved
328     * @return list of the found values.
329     */
330    public static List<String> getValuesFromElement(String valueName, Element node) {
331        List<String> attributeValues = new LinkedList<>();
332
333        if (node != null) {
334            // first check attributes of the node
335            Attr attribute = node.getAttributeNode(valueName);
336            if (attribute != null) {
337                attributeValues.add(attribute.getValue());
338            }
339            else {
340                // now check elements
341                List<Element> elements = getMatchingChildNode(valueName, node);
342                for (Element element : elements) {
343                    attributeValues.add(element.getTextContent());
344                }
345            }
346        }
347
348        return attributeValues;
349    }
350
351    /**
352     * <p>
353     * Allows the removal of pre- and suffixes from SOAP operation names in
354     * {@link SimpleSOAPEventType}.
355     * </p>
356     *
357     * @param sequences
358     *            sequences where the operation names are normalized
359     */
360    public static Collection<List<Event>> normalizeOperationNames(Collection<List<Event>> sequences,
361                                                                  String prefixToRemove,
362                                                                  String suffixToRemove)
363    {
364        Collection<List<Event>> normalizedSequences = new LinkedList<>();
365        for (List<Event> sequence : sequences) {
366            List<Event> normalizedSequence = new LinkedList<>();
367            for (Iterator<Event> eventIter = sequence.iterator(); eventIter.hasNext();) {
368                Event event = eventIter.next();
369                if ((event.getType() instanceof SimpleSOAPEventType)) {
370                    SimpleSOAPEventType eventType = (SimpleSOAPEventType) event.getType();
371                    String methodName = eventType.getCalledMethod();
372                    if (prefixToRemove != null && methodName.startsWith(prefixToRemove)) {
373                        methodName =
374                            methodName.substring(prefixToRemove.length(), methodName.length());
375                        // remove prefix
376                    }
377                    if (suffixToRemove != null && methodName.endsWith(suffixToRemove)) {
378                        methodName =
379                            methodName.substring(0, methodName.length() - suffixToRemove.length());
380                    }
381                    event =
382                        new Event(new SimpleSOAPEventType(methodName, eventType.getServiceName(),
383                                                          eventType.getClientName(),
384                                                          eventType.getSoapRequestBody(),
385                                                          eventType.getEqualSOAPDataMap()),
386                                  event.getTarget());
387                }
388                normalizedSequence.add(event);
389            }
390            normalizedSequences.add(normalizedSequence);
391        }
392        return normalizedSequences;
393    }
394
395    /**
396     * <p>
397     * Sorts the sequences by the orderingId of the requests/responses. This function only supports
398     * the ordering of {@link Event}s with a {@link SOAPEventType}.
399     * </p>
400     *
401     * @param sequences
402     *            sequences to be order
403     * @param orderType
404     *            determines if sequences are ordered by request or response
405     * @return sorted sequences
406     */
407    public static Collection<List<Event>> sortSequences(Collection<List<Event>> sequences,
408                                                        SequenceOrder orderType)
409    {
410        Collection<List<Event>> sortedSequences = new LinkedList<>();
411        for (List<Event> sequence : sequences) {
412            // use insertion sort
413            List<Event> sortedSequence = new LinkedList<>();
414            long lastOrderId = Long.MIN_VALUE;
415            long selectedOrderId;
416            Event selectedEvent;
417            while (sortedSequence.size() != sequence.size()) {
418                selectedEvent = null;
419                selectedOrderId = Long.MAX_VALUE;
420                for (Event event : sequence) {
421                    if (!(event.getType() instanceof SOAPEventType)) {
422                        throw new RuntimeException(
423                                                   "Can only order SOAPEventTypes. SimpleSOAPEvent is also not supported. Event type found: " +
424                                                       event.getType().getClass().getName());
425                    }
426                    SOAPEventType soapEventType = (SOAPEventType) event.getType();
427                    long eventOrderId;
428                    switch (orderType)
429                    {
430                        case REQUEST:
431                            eventOrderId = soapEventType.getExchange().getRequest().getOrderingId();
432                            break;
433                        case RESPONSE:
434                            eventOrderId =
435                                soapEventType.getExchange().getResponse().getOrderingId();
436                            break;
437                        default:
438                            throw new RuntimeException("unsupported order type: " +
439                                orderType.toString());
440                    }
441                    if (eventOrderId > lastOrderId && eventOrderId < selectedOrderId) {
442                        selectedOrderId = eventOrderId;
443                        selectedEvent = event;
444                    }
445                }
446                if (selectedEvent != null) {
447                    sortedSequence.add(selectedEvent);
448                    lastOrderId = selectedOrderId;
449                }
450                else {
451                    throw new RuntimeException(
452                                               "could not select next event; possibly the same order Id for multiple exchanges events!?");
453                }
454            }
455            sortedSequences.add(sortedSequence);
456        }
457        return sortedSequences;
458    }
459   
460    /**
461     * <p>
462     * Removes calls to and from all ignored services from the sequences.
463     * </p>
464     *
465     * @param sequences sequences where the ignored services are removed
466     * @param ignoredServicesString comma separted string that defines the ignored services
467     * @return sequences without events that reference the ignored services
468     */
469    public static Collection<List<Event>> removeCallsToIgnoredServices(Collection<List<Event>> sequences, String ignoredServicesString) {
470        Set<String> ignoredServices = new HashSet<>();
471        if (ignoredServicesString != null) {
472            for (String service : ignoredServicesString.split(",")) {
473                ignoredServices.add(service.trim());
474            }
475        }
476        return removeCallsToIgnoredServices(sequences, ignoredServices);
477    }
478   
479    /**
480     * <p>
481     * Removes calls to and from all ignored services from the sequences.
482     * </p>
483     *
484     * @param sequences sequences where the ignored services are removed
485     * @param ignoredServices set with all ignored service names
486     * @return sequences without events that reference the ignored services
487     */
488    public static Collection<List<Event>> removeCallsToIgnoredServices(Collection<List<Event>> sequences, Set<String> ignoredServices) {
489        Collection<List<Event>> onlyAcceptedServicesSequences = new LinkedList<>();
490        for (List<Event> sequence : sequences) {
491            List<Event> onlyAcceptedServicesSequence = new LinkedList<>();
492            for (Event event : sequence) {
493                SimpleSOAPEventType eventType = (SimpleSOAPEventType) event.getType();
494                if (!ignoredServices.contains(eventType.getServiceName()) &&
495                    !ignoredServices.contains(eventType.getClientName()) ) {
496                    onlyAcceptedServicesSequence.add(event);
497                }
498            }
499            onlyAcceptedServicesSequences.add(onlyAcceptedServicesSequence);
500        }
501        return onlyAcceptedServicesSequences;
502    }
503
504    /**
505     * <p>
506     * prevent instantiation
507     * </p>
508     */
509    private SOAPUtils() {}
510}
Note: See TracBrowser for help on using the repository browser.