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

Last change on this file since 2003 was 2003, checked in by sherbold, 9 years ago
  • extended SOAP utils with function that removes all invalid request/response pairs from a sequence of SimpleSOAPEvents
  • Property svn:mime-type set to text/plain
File size: 33.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;
25import java.util.Stack;
26import java.util.logging.Level;
27
28import javax.xml.transform.Transformer;
29import javax.xml.transform.TransformerException;
30import javax.xml.transform.TransformerFactory;
31import javax.xml.transform.TransformerFactoryConfigurationError;
32import javax.xml.transform.dom.DOMSource;
33import javax.xml.transform.stream.StreamResult;
34
35import org.w3c.dom.Attr;
36import org.w3c.dom.Element;
37import org.w3c.dom.Node;
38import org.w3c.dom.NodeList;
39
40import de.ugoe.cs.autoquest.eventcore.Event;
41import de.ugoe.cs.autoquest.plugin.http.eventcore.EqualSOAPDataMap;
42import de.ugoe.cs.autoquest.plugin.http.eventcore.SOAPEventType;
43import de.ugoe.cs.autoquest.plugin.http.eventcore.SimpleSOAPEventType;
44import de.ugoe.cs.autoquest.plugin.http.eventcore.SimpleSOAPEventType.CallType;
45import de.ugoe.cs.util.console.Console;
46
47/**
48 * <p>
49 * Utilities for working with SOAP events. Their main task is to simply working with
50 * {@link SimpleSOAPEventType} and {@link SOAPEventType}.
51 * </p>
52 *
53 * @author Steffen Herbold
54 */
55public class SOAPUtils {
56
57    /**
58     * <p>
59     * Defines how sequences are ordered:
60     * <ul>
61     * <li>REQUEST: by the request order id</li>
62     * <li>RESPONSE: by the response order id</li>
63     * </ul>
64     * </p>
65     *
66     * @author Steffen Herbold
67     */
68    public enum SequenceOrder {
69        REQUEST, RESPONSE
70    }
71
72    /**
73     * <p>
74     * Replaces events with a {@link SOAPEventType} with new events of {@link SimpleSOAPEventType}
75     * and no target.
76     * </p>
77     *
78     * @param sequences
79     *            sequences where the events are replaced
80     * @param keepOnlySOAPEvents
81     *            if true, all events that are not of SOAPEventType or SimpleSOAPEventType are
82     *            removed
83     * @return sequences with {@link SimpleSOAPEventType}s instead of {@link SOAPEventType}s
84     */
85    public static Collection<List<Event>> convertToSimpleSOAPEvent(Collection<List<Event>> sequences,
86                                                                   boolean keepOnlySOAPEvents)
87    {
88        Collection<List<Event>> newSequences = new LinkedList<>();
89        EqualSOAPDataMap equalSOAPDataMap = new EqualSOAPDataMap();
90        for (List<Event> sequence : sequences) {
91            List<Event> newSequence = null;
92            if (sequence != null) {
93                newSequence = new LinkedList<>();
94                for (Event event : sequence) {
95                    if (event.getType() instanceof SOAPEventType) {
96                        SOAPEventType eventType = (SOAPEventType) event.getType();
97                        newSequence.add(new Event(new SimpleSOAPEventType(eventType
98                            .getCalledMethod(), eventType.getServiceName(), eventType
99                            .getClientName(), eventType.getSoapRequestBody(), equalSOAPDataMap,
100                                                                          CallType.REQUEST)));
101                    }
102                    else {
103                        if (!keepOnlySOAPEvents || event.getType() instanceof SimpleSOAPEventType) {
104                            newSequence.add(event);
105                        }
106                    }
107                }
108            }
109            newSequences.add(newSequence);
110        }
111        return newSequences;
112    }
113
114    /**
115     * <p>
116     * Removes all non SOAP events from the contained sequences
117     * </p>
118     *
119     * @param sequences
120     *            sequences where the events are replaced
121     */
122    public static Collection<List<Event>> removeNonSOAPEvents(Collection<List<Event>> sequences) {
123        Collection<List<Event>> soapOnlySequences = new LinkedList<>();
124        for (List<Event> sequence : sequences) {
125            soapOnlySequences.add(removeNonSOAPEvents(sequence));
126        }
127        return soapOnlySequences;
128    }
129
130    /**
131     * <p>
132     * Removes all non SOAP events from a sequence
133     * </p>
134     *
135     * @param sequence
136     *            sequence where the events are replaced
137     */
138    public static List<Event> removeNonSOAPEvents(List<Event> sequence) {
139        List<Event> soapOnlySequence = new LinkedList<>();
140        for (Event event : sequence) {
141            if (event.getType() instanceof SOAPEventType) {
142                soapOnlySequence.add(event);
143            }
144        }
145        return soapOnlySequence;
146    }
147
148    /**
149     * <p>
150     * Helper function to get the name of a service from a SOAP event.
151     * </p>
152     *
153     * @param event
154     *            event for which the service name is retrieved
155     * @return service name
156     */
157    public static String getServiceNameFromEvent(Event event) {
158        if (event.getType() instanceof SOAPEventType) {
159            return ((SOAPEventType) event.getType()).getServiceName();
160        }
161        else if (event.getType() instanceof SimpleSOAPEventType) {
162            return ((SimpleSOAPEventType) event.getType()).getServiceName();
163        }
164        else {
165            throw new RuntimeException(
166                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
167                                           event.getType().getClass().getName());
168        }
169    }
170
171    /**
172     *
173     * <p>
174     * Helper function to get the called method from a SOAP event
175     * </p>
176     *
177     * @param event
178     *            event for which the called method is retrieved
179     * @return called method
180     */
181    public static String getCalledMethodFromEvent(Event event) {
182        if (event.getType() instanceof SOAPEventType) {
183            return ((SOAPEventType) event.getType()).getCalledMethod();
184        }
185        else if (event.getType() instanceof SimpleSOAPEventType) {
186            return ((SimpleSOAPEventType) event.getType()).getCalledMethod();
187        }
188        else {
189            throw new RuntimeException(
190                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
191                                           event.getType().getClass().getName());
192        }
193    }
194
195    /**
196     * <p>
197     * Helper function to get the name of a client from a SOAP event.
198     * </p>
199     *
200     * @param event
201     *            event for which the client name is retrieved
202     * @return service name
203     */
204    public static String getClientNameFromEvent(Event event) {
205        if (event.getType() instanceof SOAPEventType) {
206            return ((SOAPEventType) event.getType()).getClientName();
207        }
208        else if (event.getType() instanceof SimpleSOAPEventType) {
209            return ((SimpleSOAPEventType) event.getType()).getClientName();
210        }
211        else {
212            throw new RuntimeException(
213                                       "Wrong event type. Only SOAPEventType and SimpleSOAPEventType supported but was: " +
214                                           event.getType().getClass().getName());
215        }
216    }
217
218    /**
219     * <p>
220     * Helper function to get the body of a SOAP request.
221     * </p>
222     *
223     * @param event
224     *            event for which the SOAP request body is retrieved
225     * @return body of the SOAP event
226     */
227    public static Element getSoapBodyFromEvent(Event event) {
228        return getSoapBodyFromEvent(event, false, CallType.REQUEST);
229    }
230
231    /**
232     * <p>
233     * Helper function to get the body of a SOAP message.
234     * </p>
235     *
236     * @param event
237     *            event for which the SOAP message body is retrieved
238     * @param useRandomBodies
239     *            defines if random message bodies are used or the body of the associated event
240     * @param callType
241     *            defines if the request or response of a message is retrieved
242     * @return body of the SOAP event
243     */
244    public static Element getSoapBodyFromEvent(Event event,
245                                               boolean useRandomBodies,
246                                               CallType callType)
247    {
248        if (event.getType() instanceof SOAPEventType) {
249            switch (callType)
250            {
251                case REQUEST:
252                    return ((SOAPEventType) event.getType()).getSoapRequestBody();
253                case RESPONSE:
254                    return ((SOAPEventType) event.getType()).getSoapResponseBody();
255                default:
256                    throw new RuntimeException("unsupported call type: " + callType);
257            }
258        }
259        else if (event.getType() instanceof SimpleSOAPEventType) {
260            switch (callType)
261            {
262                case REQUEST:
263                    if (((SimpleSOAPEventType) event.getType()).getCallType() == CallType.REQUEST) {
264                        if (useRandomBodies) {
265                            return ((SimpleSOAPEventType) event.getType()).getRandomSoapMsgBody();
266                        }
267                        else {
268                            return ((SimpleSOAPEventType) event.getType()).getSoapMsgBody();
269                        }
270                    }
271                    else {
272                        throw new RuntimeException(
273                                                   "cannot retrieve request body, is of CallType: " +
274                                                       ((SimpleSOAPEventType) event.getType())
275                                                           .getCallType());
276                    }
277                case RESPONSE:
278                    if (((SimpleSOAPEventType) event.getType()).getCallType() == CallType.RESPONSE)
279                    {
280                        if (useRandomBodies) {
281                            return ((SimpleSOAPEventType) event.getType()).getRandomSoapMsgBody();
282                        }
283                        else {
284                            return ((SimpleSOAPEventType) event.getType()).getSoapMsgBody();
285                        }
286                    }
287                    else {
288                        throw new RuntimeException(
289                                                   "cannot retrieve response body, is of CallType: " +
290                                                       ((SimpleSOAPEventType) event.getType())
291                                                           .getCallType());
292                    }
293                default:
294                    throw new RuntimeException("unsupported call type: " + callType);
295            }
296        }
297        else {
298            throw new RuntimeException(
299                                       "unsupported event type; must be SOAPEventType or SimpleSOAPEventType but is: " +
300                                           event.getType().getClass().getName());
301        }
302    }
303
304    /**
305     * <p>
306     * Checks if an event is a SOAP request
307     * </p>
308     *
309     * @param event
310     *            event that is checked
311     * @return true if SOAP request; false otherwise
312     */
313    public static boolean isSOAPRequest(Event event) {
314        if (event.getType() instanceof SOAPEventType) {
315            return true;
316        }
317        else if (event.getType() instanceof SimpleSOAPEventType) {
318            return ((SimpleSOAPEventType) event.getType()).getCallType() == CallType.REQUEST;
319        }
320        else {
321            throw new RuntimeException(
322                                       "unsupported event type; must be SOAPEventType or SimpleSOAPEventType but is: " +
323                                           event.getType().getClass().getName());
324        }
325    }
326
327    /**
328     * <p>
329     * Checks if an event is a SOAP response
330     * </p>
331     *
332     * @param event
333     *            event that is checked
334     * @return true if SOAP response; false otherwise
335     */
336    public static boolean isSOAPResponse(Event event) {
337        if (event.getType() instanceof SOAPEventType) {
338            return true;
339        }
340        else if (event.getType() instanceof SimpleSOAPEventType) {
341            return ((SimpleSOAPEventType) event.getType()).getCallType() == CallType.RESPONSE;
342        }
343        else {
344            throw new RuntimeException(
345                                       "unsupported event type; must be SOAPEventType or SimpleSOAPEventType but is: " +
346                                           event.getType().getClass().getName());
347        }
348    }
349
350    /**
351     * <p>
352     * returns the XML serialization of a DOM node; located here because it is used for SOAP request
353     * bodies
354     * </p>
355     *
356     * @param node
357     *            DOM node that is serialized
358     * @return XML serialization as String; null if node is null
359     */
360    public static String getSerialization(Node node) {
361        if (node == null) {
362            return null;
363        }
364        try {
365            StringWriter writer = new StringWriter();
366            Transformer transformer = TransformerFactory.newInstance().newTransformer();
367            transformer.transform(new DOMSource(node), new StreamResult(writer));
368            return writer.toString();
369        }
370        catch (TransformerFactoryConfigurationError | TransformerException e) {
371            throw new IllegalArgumentException(
372                                               "could not create serialization for SOAP request body.",
373                                               e);
374        }
375    }
376
377    /**
378     * <p>
379     * Fetches all {@link Element}s that are direct children of the parent node, whose name matches
380     * the given name.
381     * </p>
382     *
383     * @param typeNameRaw
384     *            name of elements that are looked for
385     * @param parentNode
386     *            DOM node in which the elements are searched for
387     * @return found {@link Element}s
388     */
389    public static List<Element> getMatchingChildNode(String typeNameRaw, Element parentNode) {
390        List<Element> matchingNodes = new ArrayList<>();
391        Node parameterNode = null;
392        if (parentNode != null) {
393            NodeList parameterNodes = parentNode.getChildNodes();
394            String[] typeNameSplit = typeNameRaw.split(":");
395            String typeName = typeNameSplit[typeNameSplit.length - 1];
396            for (int i = 0; i < parameterNodes.getLength(); i++) {
397                parameterNode = parameterNodes.item(i);
398                if (parameterNode.getNodeType() == Node.ELEMENT_NODE) {
399                    String[] parameterNodeSplit = parameterNode.getNodeName().split(":");
400                    String parameterNodeName = parameterNodeSplit[parameterNodeSplit.length - 1];
401                    if (typeName.equals(parameterNodeName)) {
402                        matchingNodes.add((Element) parameterNode);
403                    }
404                }
405            }
406        }
407        return matchingNodes;
408    }
409
410    /**
411     * <p>
412     * Returns the values found in a currentNode for a defined valueName. To this aim, this methods
413     * first determines if there is an attribute with the name "valueName". If this is the case, the
414     * value of this attribute is returned. Otherwise, the methods looks for {@link Element}s that
415     * are direct children of the provided DOM node with the given name and returns the text content
416     * of those nodes.
417     * </p>
418     * <p>
419     * In case no values can be found, an empty list is returned
420     *
421     * @param valueName
422     *            name of the value that is retrieved
423     * @param node
424     *            node for which the value is retrieved
425     * @return list of the found values.
426     */
427    public static List<String> getValuesFromElement(String valueName, Element node) {
428        List<String> attributeValues = new LinkedList<>();
429
430        if (node != null) {
431            // first check attributes of the node
432            Attr attribute = node.getAttributeNode(valueName);
433            if (attribute != null) {
434                attributeValues.add(attribute.getValue());
435            }
436            else {
437                // now check elements
438                List<Element> elements = getMatchingChildNode(valueName, node);
439                for (Element element : elements) {
440                    attributeValues.add(element.getTextContent());
441                }
442            }
443        }
444
445        return attributeValues;
446    }
447
448    /**
449     * <p>
450     * Allows the removal of pre- and suffixes from SOAP operation names in
451     * {@link SimpleSOAPEventType}.
452     * </p>
453     *
454     * @param sequences
455     *            sequences where the operation names are normalized
456     */
457    public static Collection<List<Event>> normalizeOperationNames(Collection<List<Event>> sequences,
458                                                                  String prefixToRemove,
459                                                                  String suffixToRemove)
460    {
461        Collection<List<Event>> normalizedSequences = new LinkedList<>();
462        for (List<Event> sequence : sequences) {
463            List<Event> normalizedSequence = new LinkedList<>();
464            for (Iterator<Event> eventIter = sequence.iterator(); eventIter.hasNext();) {
465                Event event = eventIter.next();
466                if ((event.getType() instanceof SimpleSOAPEventType)) {
467                    SimpleSOAPEventType eventType = (SimpleSOAPEventType) event.getType();
468                    String methodName = eventType.getCalledMethod();
469                    if (prefixToRemove != null && methodName.startsWith(prefixToRemove)) {
470                        methodName =
471                            methodName.substring(prefixToRemove.length(), methodName.length());
472                        // remove prefix
473                    }
474                    if (suffixToRemove != null && methodName.endsWith(suffixToRemove)) {
475                        methodName =
476                            methodName.substring(0, methodName.length() - suffixToRemove.length());
477                    }
478                    event =
479                        new Event(new SimpleSOAPEventType(methodName, eventType.getServiceName(),
480                                                          eventType.getClientName(),
481                                                          eventType.getSoapMsgBody(),
482                                                          eventType.getEqualSOAPDataMap(),
483                                                          eventType.getCallType()),
484                                  event.getTarget());
485                }
486                normalizedSequence.add(event);
487            }
488            normalizedSequences.add(normalizedSequence);
489        }
490        return normalizedSequences;
491    }
492
493    /**
494     * <p>
495     * Sorts the sequences by the orderingId of the requests/responses. This function only supports
496     * the ordering of {@link Event}s with a {@link SOAPEventType}.
497     * </p>
498     *
499     * @param sequences
500     *            sequences to be order
501     * @param orderType
502     *            determines if sequences are ordered by request or response
503     * @return sorted sequences
504     */
505    public static Collection<List<Event>> sortSequences(Collection<List<Event>> sequences,
506                                                        SequenceOrder orderType)
507    {
508        Collection<List<Event>> sortedSequences = new LinkedList<>();
509        for (List<Event> sequence : sequences) {
510            // use insertion sort
511            List<Event> sortedSequence = new LinkedList<>();
512            long lastOrderId = Long.MIN_VALUE;
513            long selectedOrderId;
514            Event selectedEvent;
515            while (sortedSequence.size() != sequence.size()) {
516                selectedEvent = null;
517                selectedOrderId = Long.MAX_VALUE;
518                for (Event event : sequence) {
519                    if (!(event.getType() instanceof SOAPEventType)) {
520                        throw new RuntimeException(
521                                                   "Can only order SOAPEventTypes. SimpleSOAPEvent is also not supported. Event type found: " +
522                                                       event.getType().getClass().getName());
523                    }
524                    SOAPEventType soapEventType = (SOAPEventType) event.getType();
525                    long eventOrderId;
526                    switch (orderType)
527                    {
528                        case REQUEST:
529                            eventOrderId = soapEventType.getExchange().getRequest().getOrderingId();
530                            break;
531                        case RESPONSE:
532                            eventOrderId =
533                                soapEventType.getExchange().getResponse().getOrderingId();
534                            break;
535                        default:
536                            throw new RuntimeException("unsupported order type: " +
537                                orderType.toString());
538                    }
539                    if (eventOrderId > lastOrderId && eventOrderId < selectedOrderId) {
540                        selectedOrderId = eventOrderId;
541                        selectedEvent = event;
542                    }
543                }
544                if (selectedEvent != null) {
545                    sortedSequence.add(selectedEvent);
546                    lastOrderId = selectedOrderId;
547                }
548                else {
549                    throw new RuntimeException(
550                                               "could not select next event; possibly the same order Id for multiple exchanges events!?");
551                }
552            }
553            sortedSequences.add(sortedSequence);
554        }
555        return sortedSequences;
556    }
557
558    /**
559     * <p>
560     * Sorts the sequences by the orderingId of the requests/responses. This function only supports
561     * the ordering of {@link Event}s with a {@link SOAPEventType}.
562     * </p>
563     *
564     * @param sequences
565     *            sequences to be order
566     * @param orderType
567     *            determines if sequences are ordered by request or response
568     * @return sorted sequences
569     */
570    public static Collection<List<Event>> sortAndConvertSequences(Collection<List<Event>> sequences,
571                                                                  boolean keepRequests,
572                                                                  boolean keepResponse)
573    {
574        Collection<List<Event>> sortedSequences = new LinkedList<>();
575        EqualSOAPDataMap equalSOAPDataMap = new EqualSOAPDataMap();
576        for (List<Event> sequence : sequences) {
577            // use insertion sort
578            List<Event> sortedSequence = new LinkedList<>();
579            long lastOrderId = Long.MIN_VALUE;
580            long selectedOrderId;
581            Event selectedEvent;
582            CallType selectedCallType = CallType.RESPONSE;
583            do {
584                selectedEvent = null;
585                selectedOrderId = Long.MAX_VALUE;
586                for (Event event : sequence) {
587                    if (!(event.getType() instanceof SOAPEventType)) {
588                        throw new RuntimeException(
589                                                   "Can only order SOAPEventTypes. SimpleSOAPEvent is also not supported. Event type found: " +
590                                                       event.getType().getClass().getName());
591                    }
592                    SOAPEventType soapEventType = (SOAPEventType) event.getType();
593                    long requestOrderId = soapEventType.getExchange().getRequest().getOrderingId();
594                    long responseOrderId =
595                        soapEventType.getExchange().getResponse().getOrderingId();
596                    // System.out.println(requestOrderId + " / " + responseOrderId);
597
598                    if (requestOrderId > lastOrderId && requestOrderId < selectedOrderId) {
599                        selectedOrderId = requestOrderId;
600                        selectedEvent = event;
601                        selectedCallType = CallType.REQUEST;
602                    }
603                    if (responseOrderId > lastOrderId && responseOrderId < selectedOrderId) {
604                        selectedOrderId = responseOrderId;
605                        selectedEvent = event;
606                        selectedCallType = CallType.RESPONSE;
607                    }
608                }
609                if (selectedEvent != null) {
610                    SOAPEventType eventType = (SOAPEventType) selectedEvent.getType();
611                    Element soapMsgBody;
612                    switch (selectedCallType)
613                    {
614                        case REQUEST:
615                            soapMsgBody = eventType.getSoapRequestBody();
616                            break;
617                        case RESPONSE:
618                            soapMsgBody = eventType.getSoapResponseBody();
619                            break;
620                        default:
621                            throw new RuntimeException("unsupported call type: " + selectedCallType);
622                    }
623                    if ((keepRequests && selectedCallType == CallType.REQUEST) ||
624                        (keepResponse && selectedCallType == CallType.RESPONSE))
625                    {
626                        sortedSequence.add(new Event(new SimpleSOAPEventType(eventType
627                            .getCalledMethod(), eventType.getServiceName(), eventType
628                            .getClientName(), soapMsgBody, equalSOAPDataMap, selectedCallType)));
629                    }
630                    lastOrderId = selectedOrderId;
631                }
632
633            }
634            while (selectedEvent != null);
635            sortedSequences.add(sortedSequence);
636        }
637        return sortedSequences;
638    }
639
640    /**
641     * <p>
642     * Removes calls to and from all ignored services from the sequences.
643     * </p>
644     *
645     * @param sequences
646     *            sequences where the ignored services are removed
647     * @param ignoredServicesString
648     *            comma separted string that defines the ignored services
649     * @return sequences without events that reference the ignored services
650     */
651    public static Collection<List<Event>> removeCallsToIgnoredServices(Collection<List<Event>> sequences,
652                                                                       String ignoredServicesString)
653    {
654        Set<String> ignoredServices = new HashSet<>();
655        if (ignoredServicesString != null) {
656            for (String service : ignoredServicesString.split(",")) {
657                ignoredServices.add(service.trim());
658            }
659        }
660        return removeCallsToIgnoredServices(sequences, ignoredServices);
661    }
662
663    /**
664     * <p>
665     * Removes calls to and from all ignored services from the sequences.
666     * </p>
667     *
668     * @param sequences
669     *            sequences where the ignored services are removed
670     * @param ignoredServices
671     *            set with all ignored service names
672     * @return sequences without events that reference the ignored services
673     */
674    public static Collection<List<Event>> removeCallsToIgnoredServices(Collection<List<Event>> sequences,
675                                                                       Set<String> ignoredServices)
676    {
677        Collection<List<Event>> onlyAcceptedServicesSequences = new LinkedList<>();
678        for (List<Event> sequence : sequences) {
679            List<Event> onlyAcceptedServicesSequence = new LinkedList<>();
680            for (Event event : sequence) {
681                SimpleSOAPEventType eventType = (SimpleSOAPEventType) event.getType();
682                if (!ignoredServices.contains(eventType.getServiceName()) &&
683                    !ignoredServices.contains(eventType.getClientName()))
684                {
685                    onlyAcceptedServicesSequence.add(event);
686                }
687            }
688            onlyAcceptedServicesSequences.add(onlyAcceptedServicesSequence);
689        }
690        return onlyAcceptedServicesSequences;
691    }
692
693    /**
694     * <p>
695     * Drops all requests without responses as well as invalid request/response orders. Only the
696     * part of the sequence before the first invalid occurrence is kept.
697     * </p>
698     *
699     * @param sequences
700     *            sequences where the invalid pairs are dropped
701     * @return sequences with only valid and complete interactions
702     */
703    public static Collection<List<Event>> dropInvalidResponseRequestPairs(Collection<List<Event>> sequences)
704    {
705        Collection<List<Event>> validSequences = new LinkedList<>();
706        int i = 0;
707        for (List<Event> sequence : sequences) {
708            List<Event> validSequence = dropInvalidResponseRequestPairs(sequence);
709            if (validSequence.isEmpty() ||
710                (validSequence.size() <= 2 && validSequence.get(0) == Event.STARTEVENT))
711            {
712                Console.traceln(Level.INFO, "dropped sequence " + i +
713                    ": empty after removal of invalid request/response pairs");
714            }
715            validSequences.add(validSequence);
716            i++;
717        }
718        return validSequences;
719    }
720
721    /**
722     * <p>
723     * Drops all requests without responses as well as invalid request/response orders. Only the
724     * part of the sequence before the first invalid occurrence is kept.
725     * </p>
726     *
727     * @param sequence
728     *            sequence where the invalid pairs are dropped
729     * @return sequence with only valid and complete interactions
730     */
731    public static List<Event> dropInvalidResponseRequestPairs(List<Event> sequence) {
732        Stack<SimpleSOAPEventType> unrespondedRequests = new Stack<>();
733        boolean hasStartEvent = false;
734        int lastValidIndex = 0;
735        int i = 0;
736        for (Event event : sequence) {
737            if (event == Event.STARTEVENT) {
738                hasStartEvent = true;
739                lastValidIndex = i;
740            }
741            else if (event.getType() instanceof SimpleSOAPEventType) {
742                SimpleSOAPEventType currentEventType = (SimpleSOAPEventType) event.getType();
743                if (SOAPUtils.isSOAPRequest(event)) {
744                    unrespondedRequests.push(currentEventType);
745                }
746                else if (SOAPUtils.isSOAPResponse(event)) {
747                    if (unrespondedRequests.empty()) {
748                        break; // found a response without previous request; sequence invalid from
749                               // here
750                    }
751                    else {
752                        SimpleSOAPEventType lastRequest = unrespondedRequests.peek();
753                        if (isRequestResponseMatch(lastRequest, currentEventType)) {
754                            unrespondedRequests.pop();
755                            if (unrespondedRequests.empty()) {
756                                lastValidIndex = i;
757                            }
758                        }
759                    }
760
761                }
762            }
763            i++;
764        }
765        List<Event> validSequence = new LinkedList<>(sequence.subList(0, lastValidIndex + 1));
766        if (hasStartEvent) {
767            validSequence.add(Event.ENDEVENT);
768        }
769        return validSequence;
770    }
771
772    /**
773     * <p>
774     * Checks if two {@link SimpleSOAPEventType} types are equal except their {@link CallType},
775     * which must be {@value CallType#REQUEST} for the first parameter and {@link CallType#RESPONSE}
776     * for the second parameter.
777     * </p>
778     *
779     * @param request
780     *            request soap event
781     * @param response
782     *            response soap event
783     * @return true if they are a matching request/response pair
784     */
785    public static boolean isRequestResponseMatch(SimpleSOAPEventType request,
786                                                 SimpleSOAPEventType response)
787    {
788        if (request == null && response == null) {
789            return true;
790        }
791        if ((request != null && response == null) || (request == null && response != null)) {
792            return false;
793        }
794        return request.getCallType().equals(CallType.REQUEST) &&
795            response.getCallType().equals(CallType.RESPONSE) &&
796            HTTPUtils.equals(request.getCalledMethod(), response.getCalledMethod()) &&
797            HTTPUtils.equals(request.getServiceName(), response.getServiceName()) &&
798            HTTPUtils.equals(request.getClientName(), response.getClientName());
799    }
800
801    /**
802     * <p>
803     * prevent instantiation
804     * </p>
805     */
806    private SOAPUtils() {}
807}
Note: See TracBrowser for help on using the repository browser.