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    }