View Javadoc

1   /*
2    * Copyright 2006-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.kuali.rice.vc.test;
18  
19  import com.predic8.schema.ComplexType;
20  import com.predic8.schema.Sequence;
21  import com.predic8.soamodel.Difference;
22  import com.predic8.wsdl.Definitions;
23  import com.predic8.wsdl.Operation;
24  import com.predic8.wsdl.PortType;
25  import com.predic8.wsdl.WSDLParser;
26  import com.predic8.wsdl.diff.WsdlDiffGenerator;
27  import org.apache.commons.lang.StringUtils;
28  import org.apache.log4j.Logger;
29  import org.kuali.rice.core.api.config.property.Config;
30  import org.kuali.rice.core.api.config.property.ConfigContext;
31  import org.kuali.rice.core.api.lifecycle.BaseLifecycle;
32  import org.kuali.rice.core.api.lifecycle.Lifecycle;
33  import org.kuali.rice.core.framework.resourceloader.SpringResourceLoader;
34  import org.kuali.rice.test.BaselineTestCase;
35  
36  import javax.xml.namespace.QName;
37  import java.io.File;
38  import java.util.ArrayList;
39  import java.util.LinkedList;
40  import java.util.List;
41  
42  import static org.junit.Assert.assertTrue;
43  
44  
45  /*
46   *  Compatible Changes
47   *   - adding a new WSDL operation definition and associated message definitions
48   *   - adding a new WSDL port type definition and associated operation definitions
49   *   - adding new WSDL binding and service definitions
50   *   - adding a new optional XML Schema element or attribute declaration to a message definition
51   *   - reducing the constraint granularity of an XML Schema element or attribute of a message definition type
52   *   - adding a new XML Schema wildcard to a message definition type
53   *   - adding a new optional WS-Policy assertion
54   *   - adding a new WS-Policy alternative
55   *
56   * Incompatible Changes
57   *   - renaming an existing WSDL operation definition
58   *   - removing an existing WSDL operation definition
59   *   - changing the MEP of an existing WSDL operation definition
60   *   - adding a fault message to an existing WSDL operation definition
61   *   - adding a new required XML Schema element or attribute declaration to a message definition
62   *   - increasing the constraint granularity of an XML Schema element or attribute declaration of a message definition
63   *   - renaming an optional or required XML Schema element or attribute in a message definition
64   *   - removing an optional or required XML Schema element or attribute or wildcard from a message definition
65   *   - adding a new required WS-Policy assertion or expression
66   *   - adding a new ignorable WS-Policy expression (most of the time)
67   */
68  @BaselineTestCase.BaselineMode(BaselineTestCase.Mode.ROLLBACK)
69  public abstract class WsdlCompareTestCase extends BaselineTestCase {
70      private static final Logger LOG = Logger.getLogger(WsdlCompareTestCase.class);
71      private static final String WSDL_URL = "wsdl.test.previous.url";
72      private static final String WSDL_PREVIOUS_VERSION = "wsdl.test.previous.version";
73      private String wsdlUrlPrefix;
74      private String previousVersion;
75      //private static final String MODULE_NAME = "vc";
76  
77      public WsdlCompareTestCase(String moduleName) {
78          super(moduleName);
79      }
80  
81      protected void verifyWsdlDifferences(Difference diff, String level, String previousDescription) {
82          LOG.info(level + "change: " + diff.getDescription());
83          if (!diff.isSafe() && !diff.isBreaks()) {
84              LOG.info(level + "non-breaking change" + diff.getDescription());
85          } else {
86              if (diff.isBreaks()) {
87                  LOG.error(level + "breaking change: " + diff.getType() + diff.getDescription());
88              }
89          }
90  
91          assertTrue("Difference must be determined to not be breaking: " + diff.getDescription(), !diff.isBreaks());
92  
93          //check for operation based sequence changes
94          checkForOperationBasedChanges(diff);
95  
96          for (Difference moreDiff : diff.getDiffs())  {
97              verifyWsdlDifferences(moreDiff, level + "  ", diff.getDescription());
98          }
99      }
100 
101     /*
102      * This method is essentially an extra check because java2ws marks parameters on methods as minOccurs=0, which means
103      * as far as the wsdl comparison, adding a new parameter is ok, because it isn't required.
104      *
105      * Unfortunately, that adding the parameter breaks compatibility for us because it invalidates the java interface.
106      *
107      * So, This method goes through, and checks to see if the sequence change is on one of the services Operators.  If it
108      * is on an operator, and there is a difference in type of the operator, we've broken compatibility and should fail.
109      */
110     private void checkForOperationBasedChanges(Difference diff) {
111         if ("sequence".equals(diff.getType())
112                 && diff.getA() != null
113                 && diff.getB() != null) {
114             Sequence oldSequence = (Sequence)diff.getA();
115             Sequence newSequence = (Sequence)diff.getB();
116             if (newSequence.getParent() instanceof ComplexType) {
117                 ComplexType parent = (ComplexType)newSequence.getParent();
118                 String serviceName = newSequence.getSchema().getDefinitions().getName();
119                 PortType portType = newSequence.getSchema().getDefinitions().getPortType(serviceName);
120                 if (portType != null) {
121                     Operation operation = portType.getOperation(parent.getName());
122 
123                     assertTrue("Element cannot be added to a sequence if sequence is an Operation " + diff
124                             .getDescription(), operation == null);
125                 }
126             }
127         }
128     }
129 
130     protected List<Difference> compareWsdlDefinitions(String oldWsdl, String newWsdl) {
131         WSDLParser parser = new WSDLParser();
132 
133         Definitions wsdl1;
134         Definitions wsdl2 = parser.parse(newWsdl);
135         try {
136             wsdl1 = parser.parse(oldWsdl);
137         } catch (com.predic8.xml.util.ResourceDownloadException e) {
138             wsdl1 = wsdl2;
139         }
140 
141         WsdlDiffGenerator diffGen = new WsdlDiffGenerator(wsdl1, wsdl2);
142         return diffGen.compare();
143     }
144 
145     protected String getPreviousVersionWsdlUrl(String wsdlFile) {
146         if (getWsdlUrlPrefix() == null
147                 || getPreviousVersion() == null) {
148             populateWsdlUrlPrefix();
149         }
150         if (StringUtils.isBlank(getWsdlUrlPrefix()) ||
151                 StringUtils.isBlank(getPreviousVersion())) {
152             return null;
153         }
154         StringBuilder oldWsdl = new StringBuilder(getWsdlUrlPrefix());
155         oldWsdl.append("rice-");
156         oldWsdl.append(getModuleName());
157         oldWsdl.append("-api-");
158         oldWsdl.append(getPreviousVersion());
159         oldWsdl.append("-");
160         oldWsdl.append(wsdlFile);
161 
162         return oldWsdl.toString();
163     }
164 
165     //String oldWsdl = MAVEN_REPO_PREFIX + MODULE + "-api/" + PREVIOUS_VERSION + "/rice-" + MODULE + "-api-" + PREVIOUS_VERSION + "-" + file.getName();
166     private void populateWsdlUrlPrefix() {
167         String wsdlUrl = ConfigContext.getCurrentContextConfig().getProperty(WSDL_URL);
168 
169         if (StringUtils.isNotBlank(wsdlUrl)
170                 && StringUtils.isNotBlank(getPreviousVersion())) {
171             StringBuilder urlBuilder = new StringBuilder(wsdlUrl);
172             if (!wsdlUrl.endsWith("/")) {
173                 urlBuilder.append("/");
174             }
175             urlBuilder.append("rice-");
176             urlBuilder.append(getModuleName());
177             urlBuilder.append("-api/");
178             urlBuilder.append(getPreviousVersion());
179             urlBuilder.append("/");
180             setWsdlUrlPrefix(urlBuilder.toString());
181         } else {
182             setWsdlUrlPrefix("");
183         }
184     }
185 
186     protected void compareWsdlFiles(File[] files) {
187         assertTrue("There should be wsdls to compare", files != null  && files.length > 0);
188         for (File file : files) {
189             if (file.getName().endsWith(".wsdl")) {
190                 LOG.info("new wsdl: " + file.getAbsolutePath());
191                 String newWsdl = file.getAbsolutePath();
192                 String oldWsdl = getPreviousVersionWsdlUrl(file.getName());
193                 if (oldWsdl == null) {
194                     LOG.warn("Old wsdl not found.  Comparing against same version");
195                     oldWsdl = newWsdl;
196                 }
197                 LOG.info("old wsdl: " + oldWsdl + "\n");
198                 List<Difference> differences = compareWsdlDefinitions(oldWsdl, newWsdl);
199                 for (Difference diff : differences) {
200                     verifyWsdlDifferences(diff, "", diff.getDescription());
201                 }
202             }
203         }
204     }
205 
206     public String getWsdlUrlPrefix() {
207         return wsdlUrlPrefix;
208     }
209 
210     public void setWsdlUrlPrefix(String wsdlUrlPrefix) {
211         this.wsdlUrlPrefix = wsdlUrlPrefix;
212     }
213 
214     public String getPreviousVersion() {
215         if (StringUtils.isEmpty(this.previousVersion)) {
216             this.previousVersion = ConfigContext.getCurrentContextConfig().getProperty(WSDL_PREVIOUS_VERSION);
217         }
218         return this.previousVersion;
219     }
220 
221     public void setPreviousVersion(String previousVersion) {
222         this.previousVersion = previousVersion;
223     }
224 
225     @Override
226     protected Lifecycle getLoadApplicationLifecycle() {
227         SpringResourceLoader springResourceLoader = new SpringResourceLoader(new QName("VCTestHarnessResourceLoader"), "classpath:VCTestHarnessSpringBeans.xml", null);
228         springResourceLoader.setParentSpringResourceLoader(getTestHarnessSpringResourceLoader());
229         return springResourceLoader;
230     }
231 
232     @Override
233     protected List<Lifecycle> getPerTestLifecycles() {
234         return new ArrayList<Lifecycle>();
235     }
236 
237     @Override
238     protected List<Lifecycle> getSuiteLifecycles() {
239         List<Lifecycle> lifecycles = new LinkedList<Lifecycle>();
240 
241         /**
242          * Initializes Rice configuration from the test harness configuration file.
243          */
244         lifecycles.add(new BaseLifecycle() {
245             @Override
246             public void start() throws Exception {
247                 Config config = getTestHarnessConfig();
248                 ConfigContext.init(config);
249                 super.start();
250             }
251         });
252 
253         return lifecycles;
254     }
255 }