001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.soap.wsdl.validator;
018
019 import java.lang.reflect.Method;
020 import java.net.URI;
021 import java.util.Collection;
022 import java.util.HashSet;
023 import java.util.Iterator;
024 import java.util.List;
025 import java.util.ResourceBundle;
026 import java.util.Set;
027
028 import javax.wsdl.Binding;
029 import javax.wsdl.BindingFault;
030 import javax.wsdl.BindingOperation;
031 import javax.wsdl.Definition;
032 import javax.wsdl.Message;
033 import javax.wsdl.Operation;
034 import javax.wsdl.OperationType;
035 import javax.wsdl.Part;
036 import javax.wsdl.PortType;
037 import javax.wsdl.extensions.ElementExtensible;
038 import javax.wsdl.extensions.soap.SOAPBinding;
039 import javax.wsdl.extensions.soap.SOAPBody;
040 import javax.wsdl.extensions.soap.SOAPFault;
041 import javax.wsdl.extensions.soap.SOAPHeader;
042 import javax.wsdl.extensions.soap.SOAPHeaderFault;
043 import javax.wsdl.extensions.soap.SOAPOperation;
044
045 import org.apache.servicemix.soap.util.QNameUtil;
046 import org.apache.servicemix.soap.wsdl.WSDLUtils;
047
048 public class WSIBPValidator {
049
050 public enum Code {
051 R2201,
052 R2204,
053 R2205,
054 R2210,
055 R2303,
056 R2304,
057 R2306,
058 R2401,
059 R2701,
060 R2702,
061 R2705,
062 R2706,
063 R2710,
064 R2716,
065 R2717,
066 R2718,
067 R2720,
068 R2721,
069 R2726,
070 R2754,
071 }
072
073 public enum Style {
074 RPC,
075 DOCUMENT,
076 }
077
078 private Definition definition;
079 private Set<String> errors;
080 private ResourceBundle bundle;
081
082 public WSIBPValidator(Definition definition) {
083 this.definition = definition;
084 this.errors = new HashSet<String>();
085 this.bundle = ResourceBundle.getBundle("org.apache.servicemix.soap.wsdl.validator.WSIBP");
086 }
087
088 protected void error(Code code, Binding binding) {
089 error(code, "binding", QNameUtil.toString(binding.getQName()));
090 }
091
092 protected void error(Code code, PortType portType) {
093 error(code, "port-type", QNameUtil.toString(portType.getQName()));
094 }
095
096 protected void error(Code code, Message message) {
097 error(code, "message", QNameUtil.toString(message.getQName()));
098 }
099
100 protected void error(Code code, String locType, String loc) {
101 StringBuffer buf = new StringBuffer();
102 buf.append(locType);
103 buf.append(" \"");
104 buf.append(loc);
105 buf.append("\" : ");
106 buf.append("Basic Profile Violation #");
107 buf.append(code);
108 buf.append(": ");
109 buf.append(bundle.getString(code.toString()));
110 errors.add(buf.toString());
111 }
112
113 public boolean isValid() {
114 for (Method m : getClass().getMethods()) {
115 if (m.getName().startsWith("check")) {
116 try {
117 m.invoke(this, new Object[] {});
118 } catch (Exception e) {
119 throw new RuntimeException(e);
120 }
121 }
122 }
123 return errors.size() == 0;
124 }
125
126 public Collection<String> getErrors() {
127 return errors;
128 }
129
130 public void checkPortTypes() {
131 for (Iterator itPt = definition.getPortTypes().values().iterator(); itPt.hasNext();) {
132 PortType portType = (PortType) itPt.next();
133 Set<String> operationNames = new HashSet<String>();
134 for (Iterator itOp = portType.getOperations().iterator(); itOp.hasNext();) {
135 Operation operation = (Operation) itOp.next();
136 if (operation.getStyle() != OperationType.ONE_WAY && operation.getStyle() != OperationType.REQUEST_RESPONSE) {
137 error(Code.R2303, portType);
138 }
139 if (!operationNames.add(operation.getName())) {
140 error(Code.R2304, portType);
141 }
142 }
143 }
144 }
145
146 public void checkMessages() {
147 for (Iterator itMsg = definition.getMessages().values().iterator(); itMsg.hasNext();) {
148 Message msg = (Message) itMsg.next();
149 for (Iterator it2 = msg.getParts().values().iterator(); it2.hasNext();) {
150 Part p = (Part) it2.next();
151 if (p.getTypeName() != null && p.getElementName() != null) {
152 error(Code.R2306, msg);
153 }
154 }
155 }
156 }
157
158 public void checkBindings() {
159 for (Iterator itBd = definition.getBindings().values().iterator(); itBd.hasNext();) {
160 Binding binding = (Binding) itBd.next();
161 SOAPBinding soapBinding = WSDLUtils.getExtension(binding, SOAPBinding.class);
162
163 // R2401
164 if (soapBinding == null) {
165 error(Code.R2401, binding);
166 continue;
167 }
168
169 // R2701 R2702
170 if (soapBinding.getTransportURI() == null) {
171 error(Code.R2701, binding);
172 } else if (!"http://schemas.xmlsoap.org/soap/http".equals(soapBinding.getTransportURI())) {
173 error(Code.R2702, binding);
174 }
175
176 // R2706
177 for (Iterator itBop = binding.getBindingOperations().iterator(); itBop.hasNext();) {
178 BindingOperation bop = (BindingOperation) itBop.next();
179 if (!ensureLiteral(bop)) {
180 error(Code.R2706, binding);
181 break;
182 }
183 }
184
185 // R2705
186 Style bindingStyle = null;
187 for (Iterator itBop = binding.getBindingOperations().iterator(); itBop.hasNext();) {
188 BindingOperation bop = (BindingOperation) itBop.next();
189 SOAPOperation soapBop = WSDLUtils.getExtension(bop, SOAPOperation.class);
190 Style opStyle = soapBop != null ? getStyle(soapBop.getStyle()) : null;
191 if (opStyle == null) {
192 opStyle = getStyle(soapBinding.getStyle());
193 if (opStyle == null) {
194 opStyle = Style.DOCUMENT;
195 }
196 }
197 if (bindingStyle == null) {
198 bindingStyle = opStyle;
199 } else if (bindingStyle != opStyle) {
200 error(Code.R2705, binding);
201 break;
202 }
203 }
204
205 // R2201, R2204, R2209
206 if (bindingStyle == Style.DOCUMENT) {
207 for (Iterator itBop = binding.getBindingOperations().iterator(); itBop.hasNext();) {
208 BindingOperation bop = (BindingOperation) itBop.next();
209 validateDocLitBodyParts(binding,
210 WSDLUtils.getExtension(bop.getBindingInput(), SOAPBody.class),
211 bop.getOperation().getInput().getMessage());
212 validateDocLitBodyParts(binding,
213 WSDLUtils.getExtension(bop.getBindingOutput(), SOAPBody.class),
214 bop.getOperation().getOutput().getMessage());
215 }
216 }
217
218 // R2203
219 if (bindingStyle == Style.RPC) {
220
221 }
222
223 // R2205
224 for (Iterator itBop = binding.getBindingOperations().iterator(); itBop.hasNext();) {
225 BindingOperation bop = (BindingOperation) itBop.next();
226 validateHeaderParts(binding,
227 WSDLUtils.getExtensions(bop.getBindingInput(), SOAPHeader.class),
228 bop.getOperation().getInput().getMessage());
229 if (bop.getOperation().getOutput() != null) {
230 validateHeaderParts(binding,
231 WSDLUtils.getExtensions(bop.getBindingOutput(), SOAPHeader.class),
232 bop.getOperation().getOutput().getMessage());
233 }
234 /*
235 for (BindingFault fault : getBindingFaults(bop)) {
236 validateFaultParts(binding,
237 getExtension(fault, SOAPFault.class),
238 bop.getOperation().getFault(fault.getName()).getMessage());
239 }
240 */
241 }
242
243 // R2716
244 if (bindingStyle == Style.DOCUMENT) {
245 for (Iterator itBop = binding.getBindingOperations().iterator(); itBop.hasNext();) {
246 BindingOperation bop = (BindingOperation) itBop.next();
247 if (!ensureNamespace(bop, false, false) || !ensureNamespace(bop, true, false)) {
248 error(Code.R2716, binding);
249 break;
250 }
251 }
252 }
253 // R2717
254 if (bindingStyle == Style.RPC) {
255 for (Iterator itBop = binding.getBindingOperations().iterator(); itBop.hasNext();) {
256 BindingOperation bop = (BindingOperation) itBop.next();
257 if (!ensureNamespace(bop, true, true)) {
258 error(Code.R2717, binding);
259 break;
260 }
261 }
262 }
263 // R2726
264 if (bindingStyle == Style.RPC) {
265 for (Iterator itBop = binding.getBindingOperations().iterator(); itBop.hasNext();) {
266 BindingOperation bop = (BindingOperation) itBop.next();
267 if (!ensureNamespace(bop, false, false)) {
268 error(Code.R2726, binding);
269 break;
270 }
271 }
272 }
273
274 // R2718
275 {
276 Set<String> opNames = new HashSet<String>();
277 for (Iterator itOp = binding.getPortType().getOperations().iterator(); itOp.hasNext();) {
278 opNames.add(((Operation) itOp.next()).getName());
279 }
280 Set<String> bopNames = new HashSet<String>();
281 for (Iterator itBop = binding.getBindingOperations().iterator(); itBop.hasNext();) {
282 bopNames.add(((BindingOperation) itBop.next()).getName());
283 }
284 if (!opNames.equals(bopNames)) {
285 error(Code.R2718, binding);
286 }
287 }
288
289 // R2720
290 for (Iterator itBop = binding.getBindingOperations().iterator(); itBop.hasNext();) {
291 BindingOperation bop = (BindingOperation) itBop.next();
292 List<ElementExtensible> els = WSDLUtils.getElements(bop);
293 boolean error = false;
294 for (SOAPHeader sh : WSDLUtils.getExtensions(els, SOAPHeader.class)) {
295 error |= (sh.getPart() == null);
296 for (SOAPHeaderFault shf : WSDLUtils.getSOAPHeaderFaults(sh)) {
297 error |= (shf.getPart() == null);
298 }
299 }
300 if (error) {
301 error(Code.R2720, binding);
302 }
303 }
304
305 // R2721, R2754
306 for (Iterator itBop = binding.getBindingOperations().iterator(); itBop.hasNext();) {
307 BindingOperation bop = (BindingOperation) itBop.next();
308 for (BindingFault fault : WSDLUtils.getBindingFaults(bop)) {
309 for (SOAPFault sf : WSDLUtils.getExtensions(fault, SOAPFault.class)) {
310 if (sf.getName() == null) {
311 error(Code.R2721, binding);
312 } else if (!sf.getName().equals(fault.getName())) {
313 error(Code.R2754, binding);
314 }
315 }
316 }
317 }
318 }
319 }
320
321 private void validateHeaderParts(Binding binding, List<SOAPHeader> headers, Message message) {
322 for (SOAPHeader sh : headers) {
323 if (message.getPart(sh.getPart()).getElementName() == null) {
324 error(Code.R2205, binding);
325 }
326 for (SOAPHeaderFault shf : WSDLUtils.getSOAPHeaderFaults(sh)) {
327 if (message.getPart(shf.getPart()).getElementName() == null) {
328 error(Code.R2205, binding);
329 }
330 }
331 }
332 }
333
334 private void validateDocLitBodyParts(Binding binding, SOAPBody body, Message message) {
335 if (body != null) {
336 if (body.getParts() == null) {
337 if (message.getParts().size() > 1) {
338 error(Code.R2210, binding);
339 }
340 } else {
341 if (body.getParts().size() > 1) {
342 error(Code.R2201, binding);
343 }
344 for (String p : WSDLUtils.getParts(body)) {
345 if (message.getPart(p).getElementName() == null) {
346 error(Code.R2204, binding);
347 }
348 }
349 }
350 }
351 }
352
353 private boolean ensureLiteral(BindingOperation operation) {
354 // R2707: use attribute defaults to "literal"
355 List<ElementExtensible> els = WSDLUtils.getElements(operation);
356 for (SOAPBody sb : WSDLUtils.getExtensions(els, SOAPBody.class)) {
357 if (sb.getUse() == null || !WSDLUtils.WSDL1_USE_LITERAL.equals(sb.getUse())) {
358 return false;
359 }
360 }
361 for (SOAPHeader sh : WSDLUtils.getExtensions(els, SOAPHeader.class)) {
362 if (sh.getUse() == null || !WSDLUtils.WSDL1_USE_LITERAL.equals(sh.getUse())) {
363 return false;
364 }
365 for (SOAPHeaderFault shf : WSDLUtils.getSOAPHeaderFaults(sh)) {
366 if (shf.getUse() == null || !WSDLUtils.WSDL1_USE_LITERAL.equals(shf.getUse())) {
367 return false;
368 }
369 }
370 }
371 for (SOAPFault sf : WSDLUtils.getExtensions(els, SOAPFault.class)) {
372 if (sf.getUse() == null || !WSDLUtils.WSDL1_USE_LITERAL.equals(sf.getUse())) {
373 return false;
374 }
375 }
376 return true;
377 }
378
379 private boolean checkNullOrNonEmpty(String str, boolean isNonEmpty) {
380 if (isNonEmpty) {
381 if (str != null && str.length() > 0) {
382 try {
383 return new URI(str).isAbsolute();
384 } catch (Exception e) {
385 }
386 }
387 return false;
388 } else {
389 return str == null;
390 }
391 }
392
393 private boolean ensureNamespace(BindingOperation operation, boolean body, boolean set) {
394 List<ElementExtensible> els = WSDLUtils.getElements(operation);
395 if (body) {
396 for (SOAPBody sb : WSDLUtils.getExtensions(els, SOAPBody.class)) {
397 if (!checkNullOrNonEmpty(sb.getNamespaceURI(), set)) {
398 return false;
399 }
400 }
401 } else {
402 for (SOAPHeader sh : WSDLUtils.getExtensions(els, SOAPHeader.class)) {
403 if (!checkNullOrNonEmpty(sh.getNamespaceURI(), set)) {
404 return false;
405 }
406 for (SOAPHeaderFault shf : WSDLUtils.getSOAPHeaderFaults(sh)) {
407 if (!checkNullOrNonEmpty(shf.getNamespaceURI(), set)) {
408 return false;
409 }
410 }
411 }
412 for (SOAPFault sf : WSDLUtils.getExtensions(els, SOAPFault.class)) {
413 if (!checkNullOrNonEmpty(sf.getNamespaceURI(), set)) {
414 return false;
415 }
416 }
417 }
418 return true;
419 }
420
421 private Style getStyle(String str) {
422 if (WSDLUtils.WSDL1_STYLE_DOCUMENT.equalsIgnoreCase(str)) {
423 return Style.DOCUMENT;
424 } else if (WSDLUtils.WSDL1_STYLE_RPC.equalsIgnoreCase(str)) {
425 return Style.RPC;
426 } else {
427 return null;
428 }
429 }
430 }