1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.rice.vc.test;
17  
18  import com.predic8.schema.ComplexType;
19  import com.predic8.schema.Sequence;
20  import com.predic8.soamodel.Difference;
21  import com.predic8.wsdl.Definitions;
22  import com.predic8.wsdl.Operation;
23  import com.predic8.wsdl.PortType;
24  import com.predic8.wsdl.WSDLParser;
25  import com.predic8.wsdl.diff.WsdlDiffGenerator;
26  import org.apache.commons.collections.CollectionUtils;
27  import org.apache.commons.lang.StringUtils;
28  import org.apache.log4j.Logger;
29  import org.codehaus.jackson.JsonNode;
30  import org.codehaus.jackson.map.ObjectMapper;
31  import org.kuali.rice.core.api.config.property.Config;
32  import org.kuali.rice.core.api.config.property.ConfigContext;
33  import org.kuali.rice.core.api.lifecycle.BaseLifecycle;
34  import org.kuali.rice.core.api.lifecycle.Lifecycle;
35  import org.kuali.rice.core.framework.resourceloader.SpringResourceLoader;
36  import org.kuali.rice.test.BaselineTestCase;
37  
38  import javax.xml.namespace.QName;
39  import java.io.BufferedReader;
40  import java.io.File;
41  import java.io.IOException;
42  import java.io.InputStreamReader;
43  import java.net.MalformedURLException;
44  import java.net.URL;
45  import java.util.*;
46  import java.util.regex.Pattern;
47  
48  import static org.junit.Assert.assertTrue;
49  import static org.junit.Assert.fail;
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  @BaselineTestCase.BaselineMode(BaselineTestCase.Mode.ROLLBACK)
75  public abstract class WsdlCompareTestCase extends BaselineTestCase {
76      private static final Logger LOG = Logger.getLogger(WsdlCompareTestCase.class);
77      private static final String WSDL_URL = "wsdl.test.previous.url";
78      private static final String WSDL_PREVIOUS_VERSION = "wsdl.test.previous.version";
79      private static final String LINE_SEPARATOR = System.getProperty("line.separator");
80  
81      private String previousVersion;
82  
83      private static final List<String> ignoreBreakageRegexps = Arrays.asList(
84              ".*Position of any changed from .*", 
85              ".*Position of element null changed.$", 
86              " *ComplexType [^ ]* removed.$", 
87  
88              " *Element [^ ]* removed.$",   
89              " *SimpleType [^ ]* removed.$" 
90      );
91  
92      public WsdlCompareTestCase(String moduleName) {
93          super(moduleName);
94      }
95  
96      protected List<String> verifyWsdlDifferences(Difference diff, String level) {
97          List<String> results = new ArrayList<String>();
98  
99          if (diff.isBreaks() == true) {
100             boolean ignore = false;
101             for (String ignoreBreakageRegexp : ignoreBreakageRegexps) {
102                 if (diff.getDescription().matches(ignoreBreakageRegexp)) {
103                     ignore = true;
104                     break;
105                 }
106             }
107 
108             if (ignore) {
109                 LOG.info(level + "non-breaking change: " + diff.getDescription());
110             } else {
111                 LOG.error(level + "breaking change: " + diff.getType() + diff.getDescription());
112                 results.add(level + diff.getDescription());
113             }
114         } else {
115             LOG.trace(level + "trivial change: " + diff.getDescription());
116         }
117 
118         
119         String opBreakageString = checkForOperationBasedChanges(diff);
120         if (opBreakageString != null) {
121             results.add(level + opBreakageString);
122         }
123 
124         
125         String returnTypeBreakageString = checkForFormerlyVoidResponse(diff);
126         if (returnTypeBreakageString != null) {
127             results.add(level + returnTypeBreakageString);
128         }
129 
130         for (Difference moreDiff : diff.getDiffs())  {
131             List<String> childBreakages = verifyWsdlDifferences(moreDiff, level + "  ");
132             for (String childBreakage : childBreakages) {
133                 if (!diff.getDescription().trim().startsWith("Schema ")) {
134                     results.add(level + diff.getDescription() + LINE_SEPARATOR + childBreakage);
135                 } else {
136                     results.add(childBreakage);
137                 }
138             }
139         }
140 
141         return results;
142     }
143 
144     
145 
146 
147 
148 
149 
150 
151 
152 
153 
154 
155     private String checkForOperationBasedChanges(Difference diff) {
156         if ("sequence".equals(diff.getType())
157                 && diff.getA() != null
158                 && diff.getB() != null) {
159             Sequence oldSequence = (Sequence)diff.getA();
160             Sequence newSequence = (Sequence)diff.getB();
161             if (newSequence.getParent() instanceof ComplexType) {
162                 ComplexType parent = (ComplexType)newSequence.getParent();
163                 String serviceName = newSequence.getSchema().getDefinitions().getName();
164                 PortType portType = newSequence.getSchema().getDefinitions().getPortType(serviceName);
165                 if (portType != null) {
166                     Operation operation = portType.getOperation(parent.getName());
167 
168                     if (operation != null) {
169                         return "Element cannot be added to a sequence if sequence is an Operation " +
170                                 diff.getDescription();
171                     }
172 
173 
174                 }
175             }
176         }
177         return null;
178     }
179 
180     
181 
182 
183 
184 
185 
186 
187 
188 
189 
190     private String checkForFormerlyVoidResponse(Difference diff) {
191         
192 
193         if ( ! "sequence".equals(diff.getType()) || diff.getA() == null || diff.getB() == null) {
194             return null;
195         }
196 
197         Sequence oldSequence = (Sequence)diff.getA();
198         Sequence newSequence = (Sequence)diff.getB();
199 
200         if ( ! (newSequence.getParent() instanceof ComplexType)) {
201             return null;
202         }
203 
204         ComplexType parent = (ComplexType)newSequence.getParent();
205 
206         
207         if ( ! parent.getName().endsWith("Response")) {
208             return null;
209         }
210 
211         
212         if (CollectionUtils.isEmpty(oldSequence.getElements()) &&
213                 CollectionUtils.isNotEmpty(newSequence.getElements())) {
214             return "Element cannot be added to a previously empty return";
215         }
216 
217         return null;
218     }
219 
220     protected List<Difference> compareWsdlDefinitions(String oldWsdl, String newWsdl) {
221         WSDLParser parser = new WSDLParser();
222 
223         Definitions wsdl1;
224         Definitions wsdl2;
225         try {
226             wsdl1 = parser.parse(oldWsdl);
227         } catch (com.predic8.xml.util.ResourceDownloadException e) {
228             LOG.info("Couldn't download " + oldWsdl + ", maybe the service didn't exist in this version?");
229             return Collections.emptyList();
230         }
231         try {
232             wsdl2 = parser.parse(newWsdl);
233         } catch (com.predic8.xml.util.ResourceDownloadException e) {
234             LOG.info("Couldn't download" + newWsdl + ", maybe the service didn't exist in this version?");
235             return Collections.emptyList();
236         }
237 
238         WsdlDiffGenerator diffGen = new WsdlDiffGenerator(wsdl1, wsdl2);
239         return diffGen.compare();
240     }
241 
242     protected String getPreviousVersionWsdlUrl(String wsdlFile, MavenVersion previousVersion) {
243 
244         StringBuilder oldWsdl = new StringBuilder(buildWsdlUrlPrefix(previousVersion.getOriginalForm()));
245         oldWsdl.append("rice-");
246         oldWsdl.append(getModuleName());
247         oldWsdl.append("-api-");
248         oldWsdl.append(previousVersion.getOriginalForm());
249         oldWsdl.append("-");
250         oldWsdl.append(wsdlFile);
251 
252         return oldWsdl.toString();
253     }
254 
255     
256     private String buildWsdlUrlPrefix(String previousVersion) {
257         String wsdlUrl = ConfigContext.getCurrentContextConfig().getProperty(WSDL_URL);
258 
259         if (StringUtils.isNotBlank(wsdlUrl)
260                 && StringUtils.isNotBlank(previousVersion)) {
261             StringBuilder urlBuilder = new StringBuilder(wsdlUrl);
262             if (!wsdlUrl.endsWith("/")) {
263                 urlBuilder.append("/");
264             }
265             urlBuilder.append("rice-");
266             urlBuilder.append(getModuleName());
267             urlBuilder.append("-api/");
268             urlBuilder.append(previousVersion);
269             urlBuilder.append("/");
270 
271             return urlBuilder.toString();
272 
273         } else {
274             throw new RuntimeException("Couldn't build wsdl url prefix");
275         }
276     }
277 
278     
279 
280 
281 
282 
283 
284 
285     protected Map<String, List<VersionTransition>> getWsdlVersionTransitionBlacklists() {
286         return new HashMap<String, List<VersionTransition>>();
287     }
288 
289     protected void compareWsdlFiles(File[] wsdlFiles) {
290         List<VersionCompatibilityBreakage> breakages = new ArrayList<VersionCompatibilityBreakage>();
291 
292         assertTrue("There should be wsdls to compare", wsdlFiles != null  && wsdlFiles.length > 0);
293 
294         MavenVersion previousVersion = new MavenVersion(getPreviousVersion(),
295                 "0" );
296         MavenVersion currentVersion = getCurrentMavenVersion();
297         List<MavenVersion> versions = getVersionRange(previousVersion, currentVersion);
298         List<VersionTransition> transitions = generateVersionTransitions(currentVersion, versions);
299 
300         for (File wsdlFile : wsdlFiles) { 
301             if (wsdlFile.getName().endsWith(".wsdl")) {
302                 LOG.info("TESTING WSDL: " + wsdlFile.getAbsolutePath());
303                 String newWsdl = wsdlFile.getAbsolutePath();
304 
305                 
306                 List<VersionTransition> wsdlTransitionBlacklist =
307                         getWsdlVersionTransitionBlacklists().get(getServiceNameFromWsdlFile(wsdlFile));
308 
309                 if (wsdlTransitionBlacklist == null) { wsdlTransitionBlacklist = Collections.emptyList(); }
310 
311                 for (VersionTransition transition : transitions) if (!wsdlTransitionBlacklist.contains(transition)) {
312                     breakages.addAll(testWsdlVersionTransition(currentVersion, wsdlFile, transition));
313                 } else {
314                     LOG.info("Ignoring blacklisted " + transition);
315                 }
316             }
317         }
318 
319         if (!breakages.isEmpty()) {
320             fail(buildBreakagesSummary(breakages));
321         }
322     }
323 
324     
325     String getServiceNameFromWsdlFile(File wsdlFile) {
326         String fileName = wsdlFile.getName();
327         int beginIndex = 1 + fileName.lastIndexOf('-');
328         int endIndex = fileName.lastIndexOf('.');
329 
330         return fileName.substring(beginIndex, endIndex);
331     }
332 
333     
334 
335 
336 
337 
338 
339 
340     private List<VersionCompatibilityBreakage> testWsdlVersionTransition(MavenVersion currentVersion, File wsdlFile, VersionTransition transition) {
341         List<VersionCompatibilityBreakage> breakages = new ArrayList<VersionCompatibilityBreakage>();
342 
343         String fromVersionWsdlUrl = getPreviousVersionWsdlUrl(wsdlFile.getName(), transition.getFromVersion());
344         String toVersionWsdlUrl = getPreviousVersionWsdlUrl(wsdlFile.getName(), transition.getToVersion());
345 
346         
347         if (transition.getToVersion().equals(currentVersion)) {
348             toVersionWsdlUrl = wsdlFile.getAbsolutePath();
349         }
350 
351         getPreviousVersionWsdlUrl(wsdlFile.getName(), transition.getToVersion());
352 
353         LOG.info("checking " + transition);
354 
355         if (fromVersionWsdlUrl == null) {
356             LOG.warn("SKIPPING check, wsdl not found for " + fromVersionWsdlUrl);
357         } else if (toVersionWsdlUrl == null) {
358             LOG.warn("SKIPPING check, wsdl not found for " + toVersionWsdlUrl);
359         } else {
360             List<Difference> differences = compareWsdlDefinitions(fromVersionWsdlUrl, toVersionWsdlUrl);
361             for (Difference diff : differences) {
362                 List<String> breakageStrings = verifyWsdlDifferences(diff, "");
363 
364                 for (String breakage : breakageStrings) {
365                     breakages.add(new VersionCompatibilityBreakage(
366                             transition.fromVersion, transition.toVersion,
367                             fromVersionWsdlUrl, toVersionWsdlUrl, breakage));
368                 }
369             }
370         }
371 
372         return breakages;
373     }
374 
375     
376 
377 
378 
379 
380 
381 
382 
383 
384 
385 
386 
387 
388 
389 
390 
391 
392 
393 
394 
395 
396 
397 
398 
399 
400 
401 
402 
403 
404 
405 
406 
407 
408 
409     protected List<VersionTransition> generateVersionTransitions(MavenVersion currentVersion, List<MavenVersion> versions) {
410         List<VersionTransition> results = new ArrayList<VersionTransition>();
411 
412         versions = new ArrayList<MavenVersion>(versions);
413         Collections.sort(versions, mavenVersionTimestampComparator);
414 
415         
416         Collections.reverse(versions);
417 
418         final MavenVersion currentMinorVersion = trimToMinorVersion(currentVersion);
419         MavenVersion buildingTransitionsTo = currentVersion; 
420 
421         
422         
423         Set<MavenVersion> minorVersionsFrom = new HashSet<MavenVersion>();
424 
425         for (MavenVersion version : versions) if (version.compareTo(buildingTransitionsTo) < 0) {
426             MavenVersion minorVersion = trimToMinorVersion(version);
427             if (minorVersion.equals(currentMinorVersion)) {
428                 
429                 results.add(new VersionTransition(version, buildingTransitionsTo));
430                 buildingTransitionsTo = version;
431 
432                 
433                 minorVersionsFrom.clear();
434             } else if (!minorVersionsFrom.contains(minorVersion)) {
435                 results.add(new VersionTransition(version, buildingTransitionsTo));
436                 minorVersionsFrom.add(minorVersion);
437             }
438         }
439 
440         
441         Collections.reverse(results);
442 
443         return results;
444     }
445 
446     
447 
448 
449     private MavenVersion trimToMinorVersion(MavenVersion fullVersion) {
450         return new MavenVersion(""+fullVersion.getNumbers().get(0)+"."+fullVersion.getNumbers().get(1), "0");
451     }
452 
453     protected String buildBreakagesSummary(List<VersionCompatibilityBreakage> breakages) {
454         StringBuilder errorsStringBuilder =
455                 new StringBuilder(LINE_SEPARATOR + "!!!!! Detected " + breakages.size() + " VC Breakages !!!!!"
456                         + LINE_SEPARATOR);
457 
458         MavenVersion lastOldVersion = null;
459         String lastOldWsdlUrl = "";
460 
461         for (VersionCompatibilityBreakage breakage : breakages) {
462             
463             if (lastOldVersion != breakage.oldMavenVersion || lastOldWsdlUrl != breakage.oldWsdlUrl) {
464                 lastOldVersion = breakage.oldMavenVersion;
465                 lastOldWsdlUrl = breakage.oldWsdlUrl;
466 
467                 errorsStringBuilder.append(LINE_SEPARATOR + "Old Version: " + lastOldVersion.getOriginalForm()
468                         +", wsdl: " + lastOldWsdlUrl);
469                 errorsStringBuilder.append(LINE_SEPARATOR + "New Version: " + breakage.newMavenVersion.getOriginalForm()
470                         +", wsdl: " + breakage.newWsdlUrl + LINE_SEPARATOR + LINE_SEPARATOR);
471             }
472             errorsStringBuilder.append(breakage.breakageMessage + LINE_SEPARATOR);
473         }
474         return errorsStringBuilder.toString();
475     }
476 
477     public String getPreviousVersion() {
478         if (StringUtils.isEmpty(this.previousVersion)) {
479             this.previousVersion = ConfigContext.getCurrentContextConfig().getProperty(WSDL_PREVIOUS_VERSION);
480         }
481         return this.previousVersion;
482     }
483 
484     public void setPreviousVersion(String previousVersion) {
485         this.previousVersion = previousVersion;
486     }
487 
488     @Override
489     protected Lifecycle getLoadApplicationLifecycle() {
490         SpringResourceLoader springResourceLoader = new SpringResourceLoader(new QName("VCTestHarnessResourceLoader"), "classpath:VCTestHarnessSpringBeans.xml", null);
491         springResourceLoader.setParentSpringResourceLoader(getTestHarnessSpringResourceLoader());
492         return springResourceLoader;
493     }
494 
495     @Override
496     protected List<Lifecycle> getPerTestLifecycles() {
497         return new ArrayList<Lifecycle>();
498     }
499 
500     @Override
501     protected List<Lifecycle> getSuiteLifecycles() {
502         List<Lifecycle> lifecycles = new LinkedList<Lifecycle>();
503 
504         
505 
506 
507         lifecycles.add(new BaseLifecycle() {
508             @Override
509             public void start() throws Exception {
510                 Config config = getTestHarnessConfig();
511                 ConfigContext.init(config);
512                 super.start();
513             }
514         });
515 
516         return lifecycles;
517     }
518 
519     
520 
521 
522 
523 
524 
525 
526 
527     protected List<MavenVersion> getVersionRange(MavenVersion lowestVersion, MavenVersion highestVersion) {
528         ArrayList<MavenVersion> results = new ArrayList<MavenVersion>();
529 
530         if (highestVersion.compareTo(lowestVersion) <= 0) {
531             throw new IllegalStateException("currentVersion " + highestVersion +
532                     "  is <= previousVersion " + lowestVersion);
533         }
534         List<MavenVersion> riceVersions = getRiceMavenVersions();
535 
536         for (MavenVersion riceVersion : riceVersions) {
537             if ( highestVersion.compareTo(riceVersion) > 0 &&
538                     lowestVersion.compareTo(riceVersion) <= 0 &&
539                     "".equals(riceVersion.getQualifier()) ) {
540                 results.add(riceVersion);
541             }
542         }
543 
544         return results;
545     }
546 
547     
548     
549     private static List<MavenVersion> riceMavenVersions = null;
550 
551     private static List<MavenVersion> getRiceMavenVersions() {
552         if (riceMavenVersions == null) {
553             String searchContent = getMavenSearchResults();
554             riceMavenVersions = parseSearchResults(searchContent);
555 
556             Collections.sort(riceMavenVersions, mavenVersionTimestampComparator);
557 
558             LOG.info("Published versions, sorted by timestamp:");
559             for (MavenVersion riceVersion : riceMavenVersions) {
560                 LOG.info("" + riceVersion.getTimestamp() + " " + riceVersion.getOriginalForm());
561             }
562 
563         }
564         return riceMavenVersions;
565     }
566 
567     
568 
569 
570     private MavenVersion getCurrentMavenVersion() {
571         return new MavenVersion(ConfigContext.getCurrentContextConfig().getProperty("rice.version"),
572                 ""+System.currentTimeMillis());
573     }
574 
575     private static List<MavenVersion> parseSearchResults(String searchContent) {
576         LinkedList<MavenVersion> riceVersions = new LinkedList<MavenVersion>();
577 
578         ObjectMapper mapper = new ObjectMapper();
579         JsonNode rootNode;
580         try {
581             rootNode = mapper.readTree(searchContent);
582         } catch (IOException e) {
583             throw new RuntimeException("Can't parse maven search results", e);
584         }
585         JsonNode docsNode = rootNode.get("response").get("docs");
586 
587         for (JsonNode node : docsNode) {
588             String versionStr = node.get("v").toString();
589             String timestampStr = node.get("timestamp").toString();
590             
591             riceVersions.add(new MavenVersion(versionStr.replace( "\"",""), timestampStr));
592         }
593 
594         Collections.sort(riceVersions);
595         return riceVersions;
596     }
597 
598     private static String getMavenSearchResults() {
599         
600         
601         final String mavenSearchUrlString =
602                 "http://search.maven.org/solrsearch/select?q=g:%22org.kuali.rice%22+AND+a:%22rice%22&core=gav&rows=20&wt=json";
603 
604         URL mavenSearchUrl;
605 
606         try {
607             mavenSearchUrl = new URL(mavenSearchUrlString);
608         } catch (MalformedURLException e) {
609             throw new RuntimeException("can't parse maven search url", e);
610         }
611 
612         StringBuilder contentBuilder = new StringBuilder();
613         BufferedReader contentReader;
614         try {
615             contentReader = new BufferedReader(new InputStreamReader(mavenSearchUrl.openStream()));
616             String line;
617             while (null != (line = contentReader.readLine())) {
618                 contentBuilder.append(line + LINE_SEPARATOR);
619             }
620         } catch (IOException e) {
621             throw new RuntimeException("Unable to read search results", e);
622         }
623         return contentBuilder.toString();
624     }
625 
626     
627 
628 
629     protected static class MavenVersion implements Comparable<MavenVersion> {
630         private static final Pattern PERIOD_PATTERN = Pattern.compile("\\.");
631         private final List<Integer> numbers;
632         private final String originalForm;
633         private final String qualifier;
634         private final Long timestamp;
635 
636         
637 
638 
639 
640         public MavenVersion(String versionString) {
641             this(versionString, "0");
642         }
643 
644         public MavenVersion(String versionString, String timestampString) {
645             originalForm = versionString;
646             if (versionString == null || "".equals(versionString.trim())) {
647                 throw new IllegalArgumentException("empty or null version string");
648             }
649             String versionPart;
650             int dashIndex = versionString.indexOf('-');
651             if (dashIndex != -1 && versionString.length()-1 > dashIndex) {
652                 qualifier = versionString.substring(dashIndex+1).trim();
653                 versionPart = versionString.substring(0,dashIndex);
654             } else {
655                 versionPart = versionString;
656                 qualifier = "";
657             }
658             String [] versionArray = PERIOD_PATTERN.split(versionPart);
659 
660             List<Integer> numbersBuilder = new ArrayList<Integer>(versionArray.length);
661 
662             for (String versionParticle : versionArray) {
663                 numbersBuilder.add(Integer.valueOf(versionParticle));
664             }
665 
666             numbers = Collections.unmodifiableList(numbersBuilder);
667 
668             timestamp = Long.valueOf(timestampString);
669         }
670 
671         @Override
672         public int compareTo(MavenVersion that) {
673             Iterator<Integer> thisNumbersIter = this.numbers.iterator();
674             Iterator<Integer> thatNumbersIter = that.numbers.iterator();
675 
676             while (thisNumbersIter.hasNext()) {
677                 
678                 if (!thatNumbersIter.hasNext()) return 1;
679 
680                 int numberComparison = thisNumbersIter.next().compareTo(thatNumbersIter.next());
681 
682                 
683                 if (numberComparison != 0) return numberComparison;
684             }
685             
686             if (thatNumbersIter.hasNext()) return -1;
687 
688             return compareQualifiers(this.qualifier, that.qualifier);
689         }
690 
691         private static int compareQualifiers(String thisQ, String thatQ) {
692             
693             if ("".equals(thisQ)) {
694                 if ("".equals(thatQ)) {
695                     return 0;
696                 }
697                 return 1;
698             } else if ("".equals(thatQ)) {
699                 return -1;
700             }
701 
702             return thisQ.compareTo(thatQ);
703         }
704 
705         public List<Integer> getNumbers() {
706             return Collections.unmodifiableList(numbers);
707         }
708 
709         public String getQualifier() {
710             return qualifier;
711         }
712 
713         public Long getTimestamp() {
714             return timestamp;
715         }
716 
717         public String getOriginalForm() {
718             return originalForm;
719         }
720 
721         @Override
722         public String toString() {
723             return "MavenVersion{" +
724                     originalForm +
725                     '}';
726         }
727 
728         @Override
729         public boolean equals(Object o) {
730             if (this == o) {
731                 return true;
732             }
733             if (o == null || getClass() != o.getClass()) {
734                 return false;
735             }
736 
737             final MavenVersion that = (MavenVersion) o;
738 
739             if (!originalForm.equals(that.originalForm)) {
740                 return false;
741             }
742 
743             return true;
744         }
745 
746         @Override
747         public int hashCode() {
748             return originalForm.hashCode();
749         }
750     }
751 
752     
753 
754 
755     private static final Comparator<MavenVersion> mavenVersionTimestampComparator = new Comparator<MavenVersion>() {
756         @Override
757         public int compare(MavenVersion o1, MavenVersion o2) {
758             return o1.getTimestamp().compareTo(o2.getTimestamp());
759         }
760     };
761 
762     
763 
764 
765     public static class VersionTransition {
766         private final MavenVersion fromVersion;
767         private final MavenVersion toVersion;
768 
769         public VersionTransition(MavenVersion fromVersion, MavenVersion toVersion) {
770             this.fromVersion = fromVersion;
771             this.toVersion = toVersion;
772             if (fromVersion == null) throw new IllegalArgumentException("fromVersion must not be null");
773             if (toVersion == null) throw new IllegalArgumentException("toVersion must not be null");
774         }
775 
776         public VersionTransition(String fromVersion, String toVersion) {
777             this(new MavenVersion(fromVersion), new MavenVersion(toVersion));
778         }
779 
780         private MavenVersion getFromVersion() {
781             return fromVersion;
782         }
783 
784         private MavenVersion getToVersion() {
785             return toVersion;
786         }
787 
788         @Override
789         public boolean equals(Object o) {
790             if (this == o) return true;
791             if (o == null || getClass() != o.getClass()) return false;
792 
793             VersionTransition that = (VersionTransition) o;
794 
795             if (!fromVersion.equals(that.fromVersion)) return false;
796             if (!toVersion.equals(that.toVersion)) return false;
797 
798             return true;
799         }
800 
801         @Override
802         public int hashCode() {
803             int result = fromVersion.hashCode();
804             result = 31 * result + toVersion.hashCode();
805             return result;
806         }
807 
808         @Override
809         public String toString() {
810             return "VersionTransition{" +
811                     fromVersion.getOriginalForm() +
812                     " -> " + toVersion.getOriginalForm() +
813                     '}';
814         }
815     }
816 
817     
818 
819 
820     protected static class VersionCompatibilityBreakage {
821         private final MavenVersion oldMavenVersion;
822         private final MavenVersion newMavenVersion;
823         private final String oldWsdlUrl;
824         private final String newWsdlUrl;
825         private final String breakageMessage;
826 
827         public VersionCompatibilityBreakage(MavenVersion oldMavenVersion, MavenVersion newMavenVersion, String oldWsdlUrl, String newWsdlUrl, String breakageMessage) {
828             if (oldMavenVersion == null) throw new IllegalArgumentException("oldMavenVersion must not be null");
829             if (newMavenVersion == null) throw new IllegalArgumentException("newMavenVersion must not be null");
830             if (StringUtils.isEmpty(oldWsdlUrl)) throw new IllegalArgumentException("oldWsdlUrl must not be empty/null");
831             if (StringUtils.isEmpty(newWsdlUrl)) throw new IllegalArgumentException("newWsdlUrl must not be empty/null");
832             if (StringUtils.isEmpty(breakageMessage)) throw new IllegalArgumentException("breakageMessage must not be empty/null");
833             this.oldWsdlUrl = oldWsdlUrl;
834             this.newWsdlUrl = newWsdlUrl;
835             this.oldMavenVersion = oldMavenVersion;
836             this.newMavenVersion = newMavenVersion;
837             this.breakageMessage = breakageMessage;
838         }
839 
840         @Override
841         public String toString() {
842             return "VersionCompatibilityBreakage{" +
843                     "oldMavenVersion=" + oldMavenVersion +
844                     ", newMavenVersion=" + newMavenVersion +
845                     ", oldWsdlUrl='" + oldWsdlUrl + '\'' +
846                     ", newWsdlUrl='" + newWsdlUrl + '\'' +
847                     ", breakageMessage='" + breakageMessage + '\'' +
848                     '}';
849         }
850     }
851 
852 }