View Javadoc

1   /*
2    *  soapui, copyright (C) 2005 Ole Matzura / eviware.com 
3    *
4    *  SoapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of the GNU Lesser General Public License as published by the Free Software Foundation; 
6    *  either version 2.1 of the License, or (at your option) any later version.
7    *
8    *  SoapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
11   */
12  
13  package com.eviware.soapui.impl.wsdl.support;
14  
15  import java.io.File;
16  import java.net.URI;
17  import java.net.URL;
18  import java.util.ArrayList;
19  import java.util.Arrays;
20  import java.util.List;
21  
22  import javax.xml.namespace.QName;
23  
24  import org.apache.log4j.Logger;
25  import org.apache.xmlbeans.SchemaTypeLoader;
26  import org.apache.xmlbeans.SimpleValue;
27  import org.apache.xmlbeans.XmlBeans;
28  import org.apache.xmlbeans.XmlCursor;
29  import org.apache.xmlbeans.XmlObject;
30  import org.apache.xmlbeans.XmlOptions;
31  import org.w3c.dom.Element;
32  import org.w3c.dom.Node;
33  
34  import com.eviware.soapui.SoapUI;
35  
36  /***
37   * XML-Schema related tools
38   * 
39   * @author Ole.Matzura
40   */
41  
42  public class SchemaUtils
43  {
44     private final static Logger log = Logger.getLogger( SchemaUtils.class );
45     
46     public static SchemaTypeLoader loadSchemaTypes(String wsdlUrl) throws Exception
47     {
48     	log.info( "Loading schema types from [" + wsdlUrl + "]");
49        SchemaTypeLoader schemaTypes = loadSchemaTypes( getSchemas( wsdlUrl ));
50        return schemaTypes;
51     }
52  
53     public static SchemaTypeLoader loadSchemaTypes(List<XmlObject> schemas) throws Exception
54     {
55        XmlOptions options = new XmlOptions();
56        options.setCompileNoValidation();
57        options.setCompileNoPvrRule();
58        options.setCompileDownloadUrls();
59        options.setCompileNoUpaRule();
60        
61        ArrayList errorList = new ArrayList();
62        options.setErrorListener( errorList );
63        
64        schemas.add( XmlObject.Factory.parse( SoapUI.class.getResource("/soapEncoding.xsd") ));
65  
66        try
67  		{
68  			return XmlBeans.loadXsd(
69  					schemas.toArray(new XmlObject[schemas.size()]), options);
70  		}
71  		catch (Exception e)
72  		{
73  			for( int c = 0; c < errorList.size(); c++ )
74  			{
75  				System.out.println( errorList.get( c ) );
76  			}
77  			throw e;
78  		}
79     }
80     
81     public static List<XmlObject> getSchemas( String wsdlUrl ) throws Exception
82     {
83        log.info( "loading schema types from " + wsdlUrl );
84        XmlOptions options = new XmlOptions();
85        options.setCompileNoValidation();
86        options.setCompileDownloadUrls();
87        options.setCompileNoUpaRule();
88        options.setSaveUseOpenFrag();
89        options.setSaveSyntheticDocumentElement( new QName( "http://www.w3.org/2001/XMLSchema", "schema" ));
90  
91        XmlObject xmlObject = XmlObject.Factory.parse(new URL(wsdlUrl),options);
92  
93        XmlObject[] schemas = xmlObject
94              .selectPath("declare namespace s='http://www.w3.org/2001/XMLSchema' .//s:schema");
95  
96        for (int i = 0; i < schemas.length; i++)
97        {
98           XmlCursor xmlCursor = schemas[i].newCursor();
99           String xmlText = xmlCursor.getObject().xmlText( options );
100          schemas[i] = XmlObject.Factory.parse(xmlText, options);
101          
102          schemas[i].documentProperties().setSourceName(wsdlUrl);
103          if( wsdlUrl.startsWith( "file:"))
104          	fixRelativeFileImports( schemas[i] );
105       }
106       
107       List<XmlObject> result = new ArrayList<XmlObject>( Arrays.asList( schemas ));
108       
109       XmlObject [] imports = xmlObject.selectPath("declare namespace s='http://schemas.xmlsoap.org/wsdl/' .//s:import");
110       for (int i = 0; i < imports.length; i++)
111       {
112          String location = ((Element)imports[i].getDomNode()).getAttribute( "location" );
113          if( location != null )
114          {
115             if( location.indexOf( "://") > 0 )
116             {
117                result.addAll( getSchemas( location ));
118             }
119             else
120             {
121                result.addAll( getSchemas( joinRelativeUrl( wsdlUrl, location ) ));
122             }
123          }
124       }
125       
126       return result;
127    }
128    
129    public static List<String> getDefinitionUrls( String wsdlUrl ) throws Exception
130    {
131    	List<String> result = new ArrayList<String>();
132    	result.add( wsdlUrl );
133    	
134       XmlOptions options = new XmlOptions();
135       options.setCompileNoValidation();
136       options.setCompileDownloadUrls();
137       options.setCompileNoUpaRule();
138       options.setSaveUseOpenFrag();
139       options.setSaveSyntheticDocumentElement( new QName( "http://www.w3.org/2001/XMLSchema", "schema" ));
140 
141       XmlObject xmlObject = XmlObject.Factory.parse(new URL(wsdlUrl),options);
142 
143       XmlObject [] wsdlImports = xmlObject.selectPath("declare namespace s='http://schemas.xmlsoap.org/wsdl/' .//s:import");
144       for (int i = 0; i < wsdlImports.length; i++)
145       {
146          String location = wsdlImports[i].getDomNode().getAttributes().getNamedItem( "location" ).getNodeValue();
147          if( location != null )
148          {
149             if( location.indexOf( "://") > 0 )
150             {
151                result.addAll( getDefinitionUrls( location ));
152             }
153             else
154             {
155                result.addAll( getDefinitionUrls( joinRelativeUrl( wsdlUrl, location ) ));
156             }
157          }
158       }
159       
160       XmlObject[] schemaImports = xmlObject.selectPath("declare namespace s='http://www.w3.org/2001/XMLSchema' .//s:import/@schemaLocation");
161       for (int i = 0; i < schemaImports.length; i++)
162       {
163          String location = ((SimpleValue)schemaImports[i]).getStringValue();
164          if( location != null )
165          {
166             if( location.indexOf( "://") > 0 )
167             {
168                result.addAll( getDefinitionUrls( location ));
169             }
170             else
171             {
172                result.addAll( getDefinitionUrls( joinRelativeUrl( wsdlUrl, location )));
173             }
174          }
175       }
176       
177       XmlObject[] schemaIncludes = xmlObject.selectPath("declare namespace s='http://www.w3.org/2001/XMLSchema' .//s:include/@schemaLocation");
178       for (int i = 0; i < schemaIncludes.length; i++)
179       {
180          String location = ((SimpleValue)schemaIncludes[i]).getStringValue();
181          if( location != null  )
182          {
183             if( location.indexOf( "://") > 0 )
184             {
185                result.addAll( getDefinitionUrls( location ));
186             }
187             else
188             {
189                result.addAll( getDefinitionUrls( joinRelativeUrl( wsdlUrl, location )));
190             }
191          }
192       }
193       
194       return result;
195    }
196    
197    public static String joinRelativeUrl( String root, String url )
198    {
199    	int ix = root.startsWith("file:") ? root.lastIndexOf( File.separatorChar ) : root.lastIndexOf( '/' );
200    	return root.substring( 0, ix+1 ) + url;
201    }
202 
203    /***
204     * Fixes relative xsd imports in the specified schema. The schemaLocation of an 
205     * xsd import must be an URI which is not the case if the loaded 
206     * wsdl is loaded with the "file" schema (ie from the file system) and an import is relative. 
207     */
208    
209 	private static void fixRelativeFileImports(XmlObject xmlObject) throws Exception
210 	{
211 		 XmlObject[] imports = xmlObject
212        	.selectPath("declare namespace s='http://www.w3.org/2001/XMLSchema' .//s:import");
213 		 
214 		 if( imports.length == 0 ) return;
215 
216 		 String source = xmlObject.documentProperties().getSourceName();
217 		 int ix = source.lastIndexOf( File.separatorChar );
218 		 if( ix != -1 )
219 			 source = source.substring( 0, ix+1 );
220 		 
221 		 for( int c = 0; c < imports.length; c++ )
222 		 {
223 			 Node locationNode = imports[c].getDomNode().getAttributes().getNamedItem( "schemaLocation" );
224 			 if( locationNode != null )
225 			 {
226 				 String location = locationNode.getNodeValue();
227 				 if( location != null  )
228 				 {
229 					 URI uri = new URI( location );
230 					 if( !uri.isAbsolute() )
231 					 {
232 						 locationNode.setNodeValue( source + location );
233 					 }
234 				 }
235 			 }
236 		 }
237 	}
238 }