1 /*
2 * ========================================================================
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * ========================================================================
20 */
21 package org.apache.cactus.internal.client.connector.http;
22
23 import java.net.HttpURLConnection;
24
25 import org.apache.cactus.WebRequest;
26 import org.apache.cactus.internal.HttpServiceDefinition;
27 import org.apache.cactus.internal.RequestDirectives;
28 import org.apache.cactus.internal.ServiceEnumeration;
29 import org.apache.cactus.internal.WebRequestImpl;
30 import org.apache.cactus.internal.WebTestResult;
31 import org.apache.cactus.internal.client.AssertionFailedErrorWrapper;
32 import org.apache.cactus.internal.client.ParsingException;
33 import org.apache.cactus.internal.client.ServletExceptionWrapper;
34 import org.apache.cactus.internal.client.WebTestResultParser;
35 import org.apache.cactus.internal.configuration.WebConfiguration;
36 import org.apache.cactus.internal.util.IoUtil;
37 import org.apache.cactus.util.ChainedRuntimeException;
38
39 /**
40 * Performs the steps necessary to run a test. It involves
41 * opening a first HTTP connection to a server redirector, reading the output
42 * stream and then opening a second HTTP connection to retrieve the test
43 * result.
44 *
45 * @version $Id: DefaultHttpClient.java 238991 2004-05-22 11:34:50Z vmassol $
46 */
47 public class DefaultHttpClient
48 {
49 /**
50 * Cactus configuration.
51 */
52 protected WebConfiguration configuration;
53
54 /**
55 * Initialize the Http client.
56 *
57 * @param theConfiguration the Cactus configuration
58 */
59 public DefaultHttpClient(WebConfiguration theConfiguration)
60 {
61 this.configuration = theConfiguration;
62 }
63
64 /**
65 * Calls the test method indirectly by calling the Redirector servlet and
66 * then open a second HTTP connection to retrieve the test results.
67 *
68 * @param theRequest the request containing all data to pass to the
69 * redirector servlet.
70 *
71 * @return the <code>HttpURLConnection</code> that contains the HTTP
72 * response when the test was called.
73 *
74 * @exception Throwable if an error occured in the test method or in the
75 * redirector servlet.
76 */
77 public HttpURLConnection doTest(WebRequest theRequest) throws Throwable
78 {
79 // Open the first connection to the redirector to execute the test on
80 // the server side
81 HttpURLConnection connection = callRunTest(theRequest);
82
83 // Open the second connection to get the test results
84 WebTestResult result = null;
85
86 try
87 {
88 result = callGetResult(theRequest);
89 }
90 catch (ParsingException e)
91 {
92 String url = this.configuration.getRedirectorURL(theRequest);
93 throw new ChainedRuntimeException("Failed to get the test "
94 + "results at [" + url + "]", e);
95 }
96
97 // Check if the returned result object returned contains an error or
98 // not. If yes, we need to raise an exception so that the JUnit
99 // framework can catch it
100 if (result.hasException())
101 {
102 // Wrap the exception message and stack trace into a fake
103 // exception class with overloaded <code>printStackTrace()</code>
104 // methods so that when JUnit calls this method it will print the
105 // stack trace that was set on the server side.
106 // If the error was an AssertionFailedError or ComparisonFailure
107 // then we use an instance of AssertionFailedErrorWrapper (so that
108 // JUnit recognize it is an AssertionFailedError exception and
109 // print it differently in it's runner console). Otherwise we use
110 // an instance of ServletExceptionWrapper.
111
112 // Note: We have to test the exceptions by string name as the JUnit
113 // AssertionFailedError class is unfortunately not serializable...
114
115 if ((result.getExceptionClassName().equals(
116 "junit.framework.AssertionFailedError"))
117 || (result.getExceptionClassName().equals(
118 "junit.framework.ComparisonFailure")))
119 {
120 throw new AssertionFailedErrorWrapper(
121 result.getExceptionMessage(),
122 result.getExceptionClassName(),
123 result.getExceptionStackTrace());
124 }
125 else
126 {
127 throw new ServletExceptionWrapper(
128 result.getExceptionMessage(),
129 result.getExceptionClassName(),
130 result.getExceptionStackTrace());
131 }
132 }
133
134 return connection;
135 }
136
137 /**
138 * Execute the test by calling the redirector.
139 *
140 * @param theRequest the request containing all data to pass to the
141 * redirector servlet.
142 * @return the <code>HttpURLConnection</code> that contains the HTTP
143 * response when the test was called.
144 *
145 * @exception Throwable if an error occured in the test method or in the
146 * redirector servlet.
147 */
148 private HttpURLConnection callRunTest(WebRequest theRequest)
149 throws Throwable
150 {
151 // Specify the service to call on the redirector side
152 theRequest.addParameter(HttpServiceDefinition.SERVICE_NAME_PARAM,
153 ServiceEnumeration.CALL_TEST_SERVICE.toString(),
154 WebRequest.GET_METHOD);
155
156 // Open the first connection to the redirector to execute the test on
157 // the server side
158 HttpClientConnectionHelper helper =
159 new HttpClientConnectionHelper(
160 this.configuration.getRedirectorURL(theRequest));
161
162 HttpURLConnection connection =
163 helper.connect(theRequest, this.configuration);
164
165 // Wrap the connection to ensure that all servlet output is read
166 // before we ask for results
167 connection = new AutoReadHttpURLConnection(connection);
168
169 // Trigger the transfer of data
170 connection.getInputStream();
171
172 return connection;
173 }
174
175 /**
176 * Get the test result from the redirector.
177 *
178 * @param theOriginalRequest the request that was used to run the test
179 * @return the result that was returned by the redirector.
180 *
181 * @exception Throwable if an error occured in the test method or in the
182 * redirector servlet.
183 */
184 private WebTestResult callGetResult(WebRequest theOriginalRequest)
185 throws Throwable
186 {
187 WebRequest resultsRequest = new WebRequestImpl(this.configuration);
188 RequestDirectives directives = new RequestDirectives(resultsRequest);
189 directives.setService(ServiceEnumeration.GET_RESULTS_SERVICE);
190
191 // Use the same redirector as was used by the original request
192 resultsRequest.setRedirectorName(
193 theOriginalRequest.getRedirectorName());
194
195 // Add authentication details
196 if (theOriginalRequest.getAuthentication() != null)
197 {
198 resultsRequest.setAuthentication(
199 theOriginalRequest.getAuthentication());
200 }
201
202 // Open the second connection to get the test results
203 HttpClientConnectionHelper helper =
204 new HttpClientConnectionHelper(
205 this.configuration.getRedirectorURL(resultsRequest));
206
207 HttpURLConnection resultConnection =
208 helper.connect(resultsRequest, this.configuration);
209
210 if (resultConnection.getResponseCode() != 200)
211 {
212 throw new ParsingException("Not a valid response ["
213 + resultConnection.getResponseCode() + " "
214 + resultConnection.getResponseMessage() + "]");
215 }
216
217 // Read the test result
218 WebTestResultParser parser = new WebTestResultParser();
219 return parser.parse(
220 IoUtil.getText(resultConnection.getInputStream(), "UTF-8"));
221 }
222 }