View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2007 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
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.wsdl;
14  
15  import java.io.ByteArrayInputStream;
16  import java.io.IOException;
17  import java.io.InputStream;
18  import java.net.URL;
19  import java.util.HashMap;
20  import java.util.Map;
21  
22  import org.apache.commons.httpclient.Credentials;
23  import org.apache.commons.httpclient.HostConfiguration;
24  import org.apache.commons.httpclient.HttpClient;
25  import org.apache.commons.httpclient.HttpState;
26  import org.apache.commons.httpclient.NTCredentials;
27  import org.apache.commons.httpclient.UsernamePasswordCredentials;
28  import org.apache.commons.httpclient.auth.AuthScheme;
29  import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
30  import org.apache.commons.httpclient.auth.CredentialsProvider;
31  import org.apache.commons.httpclient.auth.NTLMScheme;
32  import org.apache.commons.httpclient.auth.RFC2617Scheme;
33  import org.apache.commons.httpclient.methods.GetMethod;
34  
35  import com.eviware.soapui.SoapUI;
36  import com.eviware.soapui.impl.wsdl.support.http.HttpClientSupport;
37  import com.eviware.soapui.impl.wsdl.support.http.ProxyUtils;
38  import com.eviware.soapui.model.settings.Settings;
39  import com.eviware.soapui.settings.HttpSettings;
40  import com.eviware.soapui.support.UISupport;
41  import com.eviware.soapui.support.swing.SwingWorker;
42  import com.eviware.soapui.support.types.StringToStringMap;
43  import com.eviware.x.form.XForm;
44  import com.eviware.x.form.XFormDialog;
45  import com.eviware.x.form.XFormDialogBuilder;
46  import com.eviware.x.form.XFormFactory;
47  
48  /***
49   * WsdlLoader for URLs
50   * 
51   * @author ole.matzura
52   */
53  
54  public class UrlWsdlLoader extends WsdlLoader 
55  {
56  	private HttpState state;
57  	private GetMethod getMethod;
58  	private boolean aborted;
59  	private Map<String,byte[]> urlCache = new HashMap<String,byte[]>();
60  	private boolean finished;
61  	private boolean useWorker;
62  	
63  	public UrlWsdlLoader(String url)
64  	{
65  		super( url );
66  		state = new HttpState();
67  	}
68  	
69  	public boolean isUseWorker()
70  	{
71  		return useWorker;
72  	}
73  
74  	public void setUseWorker( boolean useWorker )
75  	{
76  		this.useWorker = useWorker;
77  	}
78  
79  	public InputStream load() throws Exception
80  	{
81  		return load( getBaseURI() );
82  	}
83  
84  	public synchronized InputStream load( String url ) throws Exception
85  	{
86  		if( urlCache.containsKey( url ))
87  		{
88  			return new ByteArrayInputStream( urlCache.get( url ) );
89  		}
90  		
91  		if( url.startsWith( "file:" ))
92  			return new URL( url ).openStream();
93  		
94  		log.debug( "Getting wsdl component from [" + url + "]" );
95  		
96  		createGetMethod(url);
97  		
98  		if( aborted )
99  			return null;
100 		
101 		LoaderWorker worker = new LoaderWorker();
102 		if( useWorker)
103 			worker.start();
104 		else
105 			worker.construct();
106 		
107 		while( !aborted && !finished )
108 		{
109 			Thread.sleep( 200 );
110 		}
111 		
112 		// wait for method to catch up - required in unit tests..
113 		while( !aborted && getMethod.getResponseBody() == null )
114 		{
115 			Thread.sleep( 200 );
116 		}
117 		
118 		try
119 		{
120 			if (aborted)
121 			{
122 				throw new Exception("Load of url [" + url + "] was aborted");
123 			}
124 			else
125 			{
126 				byte[] content = getMethod.getResponseBody();
127 				if( content != null )
128 				{
129 					if (HttpClientSupport.isZippedResponse( getMethod ))
130 					{
131 						content = HttpClientSupport.decompress( content );
132 					}
133 					
134 					urlCache.put(url, content);
135 					return new ByteArrayInputStream(content);
136 				}
137 				else
138 				{
139 					throw new Exception("Failed to load url; " + getMethod.getStatusCode() + " - " + getMethod.getStatusText());
140 				}
141 			}
142 		}
143 	
144 		finally
145 		{
146 			getMethod.releaseConnection();
147 		}		
148 	}
149 
150 	private void createGetMethod(String url)
151 	{
152 		getMethod = new GetMethod( url );
153 		getMethod.setDoAuthentication(true);
154 		getMethod.getParams().setParameter(CredentialsProvider.PROVIDER, new WsdlCredentialsProvider());
155 		
156 		if (SoapUI.getSettings().getBoolean(HttpSettings.AUTHENTICATE_PREEMPTIVELY))
157 		{
158 			HttpClientSupport.getHttpClient().getParams().setAuthenticationPreemptive(true);
159 		}
160 		else
161 		{
162 			HttpClientSupport.getHttpClient().getParams().setAuthenticationPreemptive(false);
163 		}
164 	}
165 	
166 	private final class LoaderWorker extends SwingWorker
167 	{
168 		public Object construct()
169 		{
170 			HttpClient httpClient = HttpClientSupport.getHttpClient();
171 			try
172 			{
173 				Settings soapuiSettings = SoapUI.getSettings();
174 				
175 				HttpClientSupport.applyHttpSettings( getMethod, soapuiSettings );
176 				HostConfiguration hostConfiguration = ProxyUtils.initProxySettings( soapuiSettings, state, new HostConfiguration(), getMethod.getURI().toString() );
177 				httpClient.executeMethod( hostConfiguration,	getMethod, state );
178 			}
179 			catch (Exception e)
180 			{
181 				return e;
182 			}
183 			finally
184 			{
185 				finished = true;
186 			}
187 			
188 			return null;
189 		}
190 	}
191 	
192 	public boolean abort()
193 	{
194 		if( getMethod != null )
195 			getMethod.abort();
196 		
197 		aborted = true;
198 		
199 		return true;
200 	}
201 
202 	public boolean isAborted()
203 	{
204 		return aborted;
205 	}
206 	
207 	/***
208 	 * CredentialsProvider for providing login information during WSDL loading
209 	 * 
210 	 * @author ole.matzura
211 	 */
212 
213 	public final class WsdlCredentialsProvider implements CredentialsProvider
214 	{	
215 		private XFormDialog basicDialog;
216 		private XFormDialog ntDialog;
217 
218 		public WsdlCredentialsProvider()
219 		{
220 		}
221 
222 		public Credentials getCredentials(final AuthScheme authscheme, final String host, int port, boolean proxy)
223 		throws CredentialsNotAvailableException
224 		{
225 			if (authscheme == null)
226 			{
227 				return null;
228 			}
229 			try
230 			{
231 				if (authscheme instanceof NTLMScheme)
232 				{
233 					if( hasCredentials() )
234 					{
235 						log.info( "Returning url credentials" );
236 						return new NTCredentials( getUsername(), getPassword(), host, null );
237 					}
238 					
239 					log.info(host + ":" + port + " requires Windows authentication");
240 					if( ntDialog == null )
241 					{
242 						buildNtDialog();
243 					}
244 					
245 					StringToStringMap values = new StringToStringMap();
246 					values.put( "Info", "Authentication required for [" + host + ":" + port + "]" );
247 					ntDialog.setValues( values );
248 					
249 					if( ntDialog.show() )
250 					{
251 						values = ntDialog.getValues();
252 						return new NTCredentials(values.get( "Username"), values.get( "Password"), host, values.get( "Domain"));
253 					}
254 					else
255 						throw new CredentialsNotAvailableException("Operation cancelled");
256 				}
257 				else if (authscheme instanceof RFC2617Scheme)
258 				{
259 					if( hasCredentials() )
260 					{
261 						log.info( "Returning url credentials" );
262 						return new UsernamePasswordCredentials( getUsername(), getPassword() );
263 					}
264 					
265 					log.info(host + ":" + port + " requires authentication with the realm '" + authscheme.getRealm() + "'");
266 					if( basicDialog == null )
267 						buildBasicDialog();
268 					
269 					StringToStringMap values = new StringToStringMap();
270 					values.put( "Info", "Authentication required for [" + host + ":" + port + "]" );
271 					basicDialog.setValues( values );
272 					if( basicDialog.show() )
273 					{
274 						values = basicDialog.getValues();
275 						return new UsernamePasswordCredentials(values.get( "Username"), values.get( "Password"));
276 					}
277 					else
278 						throw new CredentialsNotAvailableException("Operation cancelled");
279 
280 				}
281 				else
282 				{
283 					throw new CredentialsNotAvailableException("Unsupported authentication scheme: "
284 							+ authscheme.getSchemeName());
285 				}
286 			}
287 			catch (IOException e)
288 			{
289 				throw new CredentialsNotAvailableException(e.getMessage(), e);
290 			}
291 		}
292 
293 		private void buildBasicDialog()
294 		{
295 			XFormDialogBuilder builder = XFormFactory.createDialogBuilder( "Basic Authentication" );
296 			XForm mainForm = builder.createForm( "Basic" );
297 			mainForm.addLabel( "Info", "" );
298 			mainForm.addTextField( "Username", "Username for authentication", XForm.FieldType.TEXT ); 
299 			mainForm.addTextField( "Password", "Password for authentication", XForm.FieldType.PASSWORD ); 
300 			
301 			basicDialog = builder.buildDialog( builder.buildOkCancelActions(), 
302 					"Specify Basic Authentication Credentials", UISupport.OPTIONS_ICON );
303 		}
304 
305 		private void buildNtDialog()
306 		{
307 			XFormDialogBuilder builder = XFormFactory.createDialogBuilder( "NT Authentication" );
308 			XForm mainForm = builder.createForm( "Basic" );
309 			mainForm.addLabel( "Info", "" );
310 			mainForm.addTextField( "Username", "Username for authentication", XForm.FieldType.TEXT ); 
311 			mainForm.addTextField( "Password", "Password for authentication", XForm.FieldType.PASSWORD ); 
312 			mainForm.addTextField( "Domain", "NT Domain for authentication", XForm.FieldType.TEXT ); 
313 			
314 			ntDialog = builder.buildDialog( builder.buildOkCancelActions(), 
315 					"Specify NT Authentication Credentials", UISupport.OPTIONS_ICON );
316 		}
317 	}
318 
319 	public void close()
320 	{
321 	}
322 }