]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
ed763da4707123ad72019aa46b9349a13f6d7b9a
[tmp/jakarta-migration.git] /
1 /**
2  * This document is a part of the source code and related artifacts
3  * for CollectionSpace, an open source collections management system
4  * for museums and related institutions:
5  *
6  * http://www.collectionspace.org
7  * http://wiki.collectionspace.org
8  *
9  * Copyright © 2009 Regents of the University of California
10  *
11  * Licensed under the Educational Community License (ECL), Version 2.0.
12  * You may not use this file except in compliance with this License.
13  *
14  * You may obtain a copy of the ECL 2.0 License at
15  *
16  * https://source.collectionspace.org/collection-space/LICENSE.txt
17  *
18  * Unless required by applicable law or agreed to in writing, software
19  * distributed under the License is distributed on an "AS IS" BASIS,
20  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  * See the License for the specific language governing permissions and
22  * limitations under the License.
23  */
24 package org.collectionspace.services.client.test;
25
26 import java.io.IOException;
27 import java.io.UnsupportedEncodingException;
28 import java.util.ArrayList;
29 import javax.xml.bind.JAXBContext;
30 import javax.xml.bind.Marshaller;
31 import javax.ws.rs.core.MediaType;
32 import javax.ws.rs.core.MultivaluedMap;
33 import javax.ws.rs.core.Response;
34
35 import org.apache.commons.httpclient.Header;
36 import org.apache.commons.httpclient.HttpClient;
37 import org.apache.commons.httpclient.HttpException;
38 import org.apache.commons.httpclient.HttpMethod;
39 import org.apache.commons.httpclient.HttpStatus;
40 import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
41 import org.apache.commons.httpclient.methods.GetMethod;
42 import org.apache.commons.httpclient.methods.PostMethod;
43 import org.apache.commons.httpclient.methods.PutMethod;
44 import org.apache.commons.httpclient.methods.RequestEntity;
45 import org.apache.commons.httpclient.methods.StringRequestEntity;
46
47 import org.collectionspace.services.client.TestServiceClient;
48 import org.collectionspace.services.client.test.ServiceRequestType;
49
50 import org.jboss.resteasy.client.ClientResponse;
51
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * AbstractServiceTest, abstract base class for the client tests to be performed
57  * to test an entity or relation service.
58  *
59  * For Javadoc descriptions of this class's methods, see the ServiceTest interface.
60  */
61 public abstract class AbstractServiceTest implements ServiceTest {
62
63     final Logger logger = LoggerFactory.getLogger(AbstractServiceTest.class);
64
65     // An HTTP client, used for performing several negative (failure) tests.
66     //
67     // @TODO To be replaced with RESTeasy's ClientRequest (a higher-level API
68     // that is based on HttpClient), per Issue CSPACE-386.
69     protected HttpClient httpClient = new HttpClient();
70
71     // A base-level client, used (only) to obtain the base service URL.
72     private static final TestServiceClient serviceClient = new TestServiceClient();
73
74     // A resource identifier believed to be non-existent in actual use,
75     // used when testing service calls that reference non-existent resources.
76     protected final String NON_EXISTENT_ID = createNonExistentIdentifier();
77
78     // The HTTP status code expected to be returned in the response,
79     // from a request made to a service (where relevant).
80     int EXPECTED_STATUS_CODE = 0;
81     
82     // The generic type of service request being tested (e.g. CREATE, UPDATE, DELETE).
83     //
84     // This makes it possible to check behavior specific to that type of request,
85     // such as the set of valid status codes that may be returned.
86     ServiceRequestType REQUEST_TYPE = ServiceRequestType.NON_EXISTENT;
87
88     // ---------------------------------------------------------------
89     // CRUD tests : CREATE tests
90     // ---------------------------------------------------------------
91
92     // Success outcomes
93
94     @Override
95     public abstract void create();
96     
97     protected void setupCreate() {
98         clearSetup();
99         // Expected status code: 201 Created
100         EXPECTED_STATUS_CODE = Response.Status.CREATED.getStatusCode();
101         // Type of service request being tested
102         REQUEST_TYPE = ServiceRequestType.CREATE;
103     }
104
105     @Override
106     public abstract void createMultiple();
107     
108     // No setup required for createMultiple()
109
110     // Failure outcomes
111
112     @Override
113     public void createNull() {
114     }
115     
116     // No setup required for createNull()
117
118     @Override
119     public abstract void createWithMalformedXml();
120
121     protected void setupCreateWithMalformedXml() {
122         clearSetup();
123         // Expected status code: 400 Bad Request
124         EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
125         REQUEST_TYPE = ServiceRequestType.CREATE;
126     }
127
128     @Override
129     public abstract void createWithWrongXmlSchema();
130
131     protected void setupCreateWithWrongXmlSchema() {
132         clearSetup();
133         // Expected status code: 400 Bad Request
134         EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
135         REQUEST_TYPE = ServiceRequestType.CREATE;
136     }
137
138
139     // ---------------------------------------------------------------
140     // CRUD tests : READ tests
141     // ---------------------------------------------------------------
142
143     // Success outcomes
144     
145     @Override
146     public abstract void read();
147
148     protected void setupRead() {
149         clearSetup();
150         // Expected status code: 200 OK
151         EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
152         REQUEST_TYPE = ServiceRequestType.READ;
153     }
154
155     // Failure outcomes
156
157     @Override
158     public abstract void readNonExistent();
159
160     protected void setupReadNonExistent() {
161         clearSetup();
162         // Expected status code: 404 Not Found
163         EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
164         REQUEST_TYPE = ServiceRequestType.READ;
165     }
166
167
168     // ---------------------------------------------------------------
169     // CRUD tests : READ (list, or multiple) tests
170     // ---------------------------------------------------------------
171
172     // Success outcomes
173
174     @Override
175     public abstract void readList();
176
177     protected void setupReadList() {
178         clearSetup();
179         // Expected status code: 200 OK
180         EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
181         REQUEST_TYPE = ServiceRequestType.READ_MULTIPLE;
182     }
183
184     // Failure outcomes
185
186     // None tested at present.
187
188
189     // ---------------------------------------------------------------
190     // CRUD tests : UPDATE tests
191     // ---------------------------------------------------------------
192
193     // Success outcomes
194     // ----------------
195
196     @Override
197     public abstract void update();
198
199     protected void setupUpdate() {
200         clearSetup();
201         // Expected status code: 200 OK
202         EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
203         REQUEST_TYPE = ServiceRequestType.UPDATE;
204     }
205
206     // Failure outcomes
207
208     @Override
209     public abstract void updateWithMalformedXml();
210
211     protected void setupUpdateWithMalformedXml() {
212         clearSetup();
213         // Expected status code: 400 Bad Request
214         EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
215         REQUEST_TYPE = ServiceRequestType.UPDATE;
216     }
217
218     @Override
219     public abstract void updateWithWrongXmlSchema();
220
221     protected void setupUpdateWithWrongXmlSchema() {
222         clearSetup();
223         // Expected status code: 400 Bad Request
224         EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
225         REQUEST_TYPE = ServiceRequestType.UPDATE;
226     }
227
228     @Override
229     public abstract void updateNonExistent();
230
231     protected void setupUpdateNonExistent() {
232         clearSetup();
233         // Expected status code: 404 Not Found
234         EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
235         REQUEST_TYPE = ServiceRequestType.UPDATE;
236     }
237
238
239     // ---------------------------------------------------------------
240     // CRUD tests : DELETE tests
241     // ---------------------------------------------------------------
242
243     // Success outcomes
244
245     @Override
246     public abstract void delete();
247     
248     protected void setupDelete() {
249         clearSetup();
250         // Expected status code: 200 OK
251         EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
252         REQUEST_TYPE = ServiceRequestType.DELETE;
253     }
254     
255     // Failure outcomes
256     
257     @Override
258     public abstract void deleteNonExistent();
259
260     protected void setupDeleteNonExistent() {
261         clearSetup();
262         // Expected status code: 404 Not Found
263         EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
264         REQUEST_TYPE = ServiceRequestType.DELETE;
265     }
266
267
268     // ---------------------------------------------------------------
269     // Abstract utility methods
270     //
271     // Must be implemented by classes that extend
272     // this abstract base class.
273     // ---------------------------------------------------------------
274
275     /**
276      * Returns the URL path component of the service.
277      *
278      * This component will follow directly after the
279      * base path, if any.
280      */
281     protected abstract String getServicePathComponent();
282
283
284     // ---------------------------------------------------------------
285     // Utility methods
286     // ---------------------------------------------------------------
287
288     /**
289      * Reinitializes setup values, to help expose any unintended reuse
290      * of those values between tests.
291      */
292     protected void clearSetup() {
293         EXPECTED_STATUS_CODE = 0;
294         REQUEST_TYPE = ServiceRequestType.NON_EXISTENT;
295     }
296
297     // @TODO Add Javadoc comments to all methods requiring them, below.
298
299     protected String invalidStatusCodeMessage(ServiceRequestType requestType, int statusCode) {
300         return 
301             "Status code '" + statusCode + "' in response is NOT within the expected set: " +
302             requestType.validStatusCodesAsString();
303     }
304     
305     protected String getServiceRootURL() {
306         return serviceClient.getBaseURL() + getServicePathComponent();
307     }
308
309     protected String getResourceURL(String resourceIdentifier) {
310         return getServiceRootURL() + "/" + resourceIdentifier;
311     }
312
313     protected int submitRequest(HttpMethod method) {
314      int statusCode = 0;
315         try {
316             statusCode = httpClient.executeMethod(method);
317         } catch(HttpException e) {
318             logger.error("Fatal protocol violation: ", e);
319         } catch(IOException e) {
320             logger.error("Fatal transport error: ", e);
321         } catch(Exception e) {
322             logger.error("Unknown exception: ", e);
323         } finally {
324             // Release the connection.
325             method.releaseConnection();
326         }
327         return statusCode;
328     }
329     
330     protected int submitRequest(EntityEnclosingMethod method, RequestEntity entity) {
331         int statusCode = 0;
332         try {
333             method.setRequestEntity(entity);
334             statusCode = httpClient.executeMethod(method);
335         } catch(HttpException e) {
336             logger.error("Fatal protocol violation: ", e);
337         } catch(IOException e) {
338             logger.error("Fatal transport error: ", e);
339         } catch(Exception e) {
340             logger.error("Unknown exception: ", e);
341         } finally {
342             // Release the connection.
343             method.releaseConnection();
344         }
345         return statusCode;
346     }
347     
348     protected StringRequestEntity getXmlEntity(String contents) {
349         if (contents == null) {
350             contents = "";
351         }
352         StringRequestEntity entity = null;
353         final String XML_DECLARATION =
354           "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
355         final String XML_CONTENT_TYPE=MediaType.APPLICATION_XML;
356         final String UTF8_CHARSET_NAME = "UTF-8";
357         try {
358             entity =
359                 new StringRequestEntity(
360                   XML_DECLARATION + contents, XML_CONTENT_TYPE, UTF8_CHARSET_NAME);
361         } catch (UnsupportedEncodingException e) {
362             logger.error("Unsupported character encoding error: ", e);
363         }
364         return entity;
365     }
366
367     protected String extractId(ClientResponse<Response> res) {
368         MultivaluedMap mvm = res.getMetadata();
369         String uri = (String) ((ArrayList) mvm.get("Location")).get(0);
370         verbose("extractId:uri=" + uri);
371         String[] segments = uri.split("/");
372         String id = segments[segments.length - 1];
373         verbose("id=" + id);
374         return id;
375     }
376
377     protected void verbose(String msg) {
378         if (logger.isDebugEnabled()) {
379             logger.debug(msg);
380         }
381     }
382
383     protected void verbose(String msg, Object o, Class clazz) {
384         try{
385             verbose(msg);
386             JAXBContext jc = JAXBContext.newInstance(clazz);
387             Marshaller m = jc.createMarshaller();
388             m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
389                     Boolean.TRUE);
390             m.marshal(o, System.out);
391         }catch(Exception e){
392             e.printStackTrace();
393         }
394     }
395
396     protected void verboseMap(MultivaluedMap map) {
397         for(Object entry : map.entrySet()){
398             MultivaluedMap.Entry mentry = (MultivaluedMap.Entry) entry;
399             verbose("    name=" + mentry.getKey() + " value=" + mentry.getValue());
400         }
401     }
402
403     protected String createIdentifier() {
404         long identifier = System.currentTimeMillis();
405         return Long.toString(identifier);
406     }
407
408     protected String createNonExistentIdentifier() {
409         return Long.toString(Long.MAX_VALUE);
410     }
411     
412 }
413
414