View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2010 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.panels.teststeps;
14  
15  import java.sql.Connection;
16  import java.sql.PreparedStatement;
17  import java.sql.ResultSet;
18  import java.sql.SQLException;
19  import java.util.List;
20  import java.util.concurrent.Future;
21  
22  import com.eviware.soapui.SoapUI;
23  import com.eviware.soapui.impl.wsdl.panels.teststeps.support.NamedParameterStatement;
24  import com.eviware.soapui.impl.wsdl.teststeps.JdbcRequestTestStep;
25  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestStepWithProperties;
26  import com.eviware.soapui.model.iface.Request;
27  import com.eviware.soapui.model.iface.Submit;
28  import com.eviware.soapui.model.iface.SubmitContext;
29  import com.eviware.soapui.model.iface.SubmitListener;
30  import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
31  import com.eviware.soapui.model.testsuite.TestProperty;
32  import com.eviware.soapui.support.SoapUIException;
33  import com.eviware.soapui.support.StringUtils;
34  import com.eviware.soapui.support.jdbc.JdbcUtils;
35  
36  public class JdbcSubmit implements Submit, Runnable
37  {
38  	public static final String JDBC_ERROR = "JDBC_ERROR";
39  	public static final String JDBC_TIMEOUT = "JDBC_TIMEOUT";
40  	private volatile Future<?> future;
41  	private SubmitContext context;
42  	private Status status;
43  	private SubmitListener[] listeners;
44  	private Exception error;
45  	private long timestamp;
46  	protected ResultSet resultSet;
47  	protected PreparedStatement statement;
48  	private Connection connection;
49  	private long timeTaken;
50  	private final JdbcRequest request;
51  	private JdbcResponse response;
52  	private String rawSql;
53  
54  	public JdbcSubmit( JdbcRequest request, SubmitContext submitContext, boolean async )
55  	{
56  		this.request = request;
57  		this.context = submitContext;
58  
59  		List<SubmitListener> regListeners = SoapUI.getListenerRegistry().getListeners( SubmitListener.class );
60  
61  		SubmitListener[] submitListeners = request.getSubmitListeners();
62  		this.listeners = new SubmitListener[submitListeners.length + regListeners.size()];
63  		for( int c = 0; c < submitListeners.length; c++ )
64  			this.listeners[c] = submitListeners[c];
65  
66  		for( int c = 0; c < regListeners.size(); c++ )
67  			this.listeners[submitListeners.length + c] = regListeners.get( c );
68  
69  		error = null;
70  		status = Status.INITIALIZED;
71  
72  		if( async )
73  			future = SoapUI.getThreadPool().submit( this );
74  		else
75  			run();
76  	}
77  
78  	public void cancel()
79  	{
80  		if( status == Status.CANCELED )
81  			return;
82  
83  		JdbcRequest.logger.info( "Canceling request.." );
84  		if( status == Status.RUNNING )
85  		{
86  			cancelQuery();
87  		}
88  
89  		status = Status.CANCELED;
90  
91  		for( int i = 0; i < listeners.length; i++ )
92  		{
93  			try
94  			{
95  				listeners[i].afterSubmit( this, context );
96  			}
97  			catch( Throwable e )
98  			{
99  				SoapUI.logError( e );
100 			}
101 		}
102 	}
103 
104 	public Exception getError()
105 	{
106 		return error;
107 	}
108 
109 	public Request getRequest()
110 	{
111 		return request;
112 	}
113 
114 	public JdbcResponse getResponse()
115 	{
116 		return response;
117 	}
118 
119 	public Status getStatus()
120 	{
121 		return status;
122 	}
123 
124 	public Status waitUntilFinished()
125 	{
126 		if( future != null )
127 		{
128 			if( !future.isDone() )
129 			{
130 				try
131 				{
132 					future.get();
133 				}
134 				catch( Exception e )
135 				{
136 					SoapUI.logError( e );
137 				}
138 			}
139 		}
140 		else
141 			throw new RuntimeException( "cannot wait on null future" );
142 
143 		return getStatus();
144 	}
145 
146 	public void run()
147 	{
148 		try
149 		{
150 			for( int i = 0; i < listeners.length; i++ )
151 			{
152 				if( !listeners[i].beforeSubmit( this, context ) )
153 				{
154 					status = Status.CANCELED;
155 					System.err.println( "listener cancelled submit.." );
156 					return;
157 				}
158 			}
159 
160 			status = Status.RUNNING;
161 			runQuery();
162 
163 			if( status != Status.CANCELED )
164 			{
165 				status = Status.FINISHED;
166 			}
167 		}
168 		catch( Exception e )
169 		{
170 			SoapUI.logError( e );
171 			error = e;
172 		}
173 		finally
174 		{
175 			if( error != null )
176 				status = Status.ERROR;
177 
178 			if( status != Status.CANCELED )
179 			{
180 				for( int i = 0; i < listeners.length; i++ )
181 				{
182 					try
183 					{
184 						listeners[i].afterSubmit( this, context );
185 					}
186 					catch( Throwable e )
187 					{
188 						SoapUI.logError( e );
189 					}
190 				}
191 			}
192 		}
193 	}
194 
195 	private void runQuery() throws Exception
196 	{
197 		prepare();
198 		load();
199 		createResponse();
200 	}
201 
202 	public void cancelQuery()
203 	{
204 		try
205 		{
206 			if( statement != null )
207 				statement.cancel();
208 		}
209 		catch( SQLException ex )
210 		{
211 			SoapUI.logError( ex );
212 		}
213 	}
214 
215 	private void getDatabaseConnection() throws SQLException, SoapUIException
216 	{
217 		JdbcRequestTestStep testStep = request.getTestStep();
218 		connection = JdbcUtils.initConnection( context, testStep.getDriver(), testStep.getConnectionString(), testStep
219 				.getPassword() );
220 		// IMPORTANT: setting as readOnly raises an exception in calling stored
221 		// procedures!
222 		// connection.setReadOnly( true );
223 	}
224 
225 	private void load() throws Exception
226 	{
227 		try
228 		{
229 			JdbcRequestTestStep testStep = request.getTestStep();
230 
231 			if( testStep.isStoredProcedure() )
232 			{
233 				timestamp = System.currentTimeMillis();
234 				statement.execute();
235 			}
236 			else
237 			{
238 				timestamp = System.currentTimeMillis();
239 				statement.execute();
240 			}
241 			timeTaken = System.currentTimeMillis() - timestamp;
242 			if( !StringUtils.isNullOrEmpty( request.getTimeout() ) && timeTaken > Long.parseLong( request.getTimeout() ) )
243 			{
244 				context.setProperty( JDBC_TIMEOUT, PropertyExpander.expandProperties( context, request.getTimeout() ) );
245 			}
246 		}
247 		catch( SQLException e )
248 		{
249 			context.setProperty( JDBC_ERROR, e );
250 			throw e;
251 		}
252 		finally
253 		{
254 			timeTaken = System.currentTimeMillis() - timestamp;
255 		}
256 	}
257 
258 	private void prepare() throws Exception
259 	{
260 		JdbcRequestTestStep testStep = request.getTestStep();
261 		getDatabaseConnection();
262 		List<TestProperty> props = testStep.getPropertyList();
263 		if( testStep.isStoredProcedure() )
264 		{
265 			rawSql = PropertyExpander.expandProperties( context, testStep.getQuery() );
266 
267 			if( !rawSql.startsWith( "{call " ) && !rawSql.endsWith( "}" ) )
268 				rawSql = "{call " + rawSql + "}";
269 
270 		}
271 		else
272 		{
273 			rawSql = PropertyExpander.expandProperties( context, testStep.getQuery() );
274 		}
275 		NamedParameterStatement p = new NamedParameterStatement( connection, rawSql );
276 		for( TestProperty testProperty : props )
277 		{
278 			String value = PropertyExpander.expandProperties( context, testProperty.getValue() );
279 			if( !testProperty.getName().equals( WsdlTestStepWithProperties.RESPONSE_AS_XML ) )
280 			{
281 				p.setString( testProperty.getName(), value );
282 			}
283 		}
284 		statement = p.getStatement();
285 
286 		try
287 		{
288 			if( !StringUtils.isNullOrEmpty( testStep.getQueryTimeout() ) )
289 			{
290 				String queryTimeout = PropertyExpander.expandProperties( testStep, testStep.getQueryTimeout() );
291 				statement.setQueryTimeout( Integer.parseInt( queryTimeout ) );
292 			}
293 		}
294 		catch( NumberFormatException e )
295 		{
296 			SoapUI.logError( e, "Problem setting timeout" );
297 		}
298 
299 		try
300 		{
301 			if( !StringUtils.isNullOrEmpty( testStep.getMaxRows() ) )
302 			{
303 				String maxRows = PropertyExpander.expandProperties( testStep, testStep.getMaxRows() );
304 				statement.setMaxRows( Integer.parseInt( maxRows ) );
305 			}
306 		}
307 		catch( NumberFormatException e )
308 		{
309 			SoapUI.logError( e, "Problem setting maxRows" );
310 		}
311 		try
312 		{
313 			if( !StringUtils.isNullOrEmpty( testStep.getFetchSize() ) )
314 			{
315 				String fetchSize = PropertyExpander.expandProperties( testStep, testStep.getFetchSize() );
316 				statement.setFetchSize( Integer.parseInt( fetchSize ) );
317 			}
318 		}
319 		catch( NumberFormatException e )
320 		{
321 			SoapUI.logError( e, "Problem setting fetchSize" );
322 		}
323 	}
324 
325 	protected String createResponse()
326 	{
327 		try
328 		{
329 			response = new JdbcResponse( request, statement, rawSql );
330 			response.setTimestamp( timestamp );
331 			response.setTimeTaken( timeTaken );
332 		}
333 		catch( Exception e )
334 		{
335 			SoapUI.logError( e );
336 		}
337 		finally
338 		{
339 			try
340 			{
341 				if( connection != null )
342 					connection.close();
343 				if( statement != null )
344 					statement.close();
345 				if( resultSet != null )
346 					resultSet.close();
347 			}
348 			catch( Exception e )
349 			{
350 			}
351 		}
352 		return null;
353 	}
354 }