]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
dd9d18cf214170cae51b6e55e5c5407bac2fb2be
[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     // Currently used for performing several negative (failure) tests.
66     //
67     // @TODO To be replaced with RESTeasy's ClientRequest, per Issue CSPACE-386.
68     protected HttpClient httpClient = new HttpClient();
69
70     // Used (only) to obtain the base service URL.
71     private final TestServiceClient serviceClient = new TestServiceClient();
72
73     // The status code expected to be returned by a test method (where relevant).
74     int EXPECTED_STATUS_CODE = 0;
75     
76     // The generic type of service request being tested (e.g. CREATE, UPDATE, DELETE).
77     //
78     // This makes it possible to check behavior specific to that type of request,
79     // such as the set of valid status codes that may be returned.
80     ServiceRequestType REQUEST_TYPE = ServiceRequestType.NON_EXISTENT;
81
82
83     // ---------------------------------------------------------------
84     // CRUD tests : CREATE tests
85     // ---------------------------------------------------------------
86
87     // Success outcomes
88
89     @Override
90     public abstract void create();
91     
92     protected void setupCreate() {
93         clearSetup();
94         // Expected status code: 201 Created
95         EXPECTED_STATUS_CODE = Response.Status.CREATED.getStatusCode();
96         // Type of service request being tested
97         REQUEST_TYPE = ServiceRequestType.CREATE;
98     }
99
100     @Override
101     public abstract void createMultiple();
102     
103     // No setup required for createMultiple()
104
105     // Failure outcomes
106
107     @Override
108     public void createNull() {
109     }
110     
111     // No setup required for createNull()
112
113     @Override
114     public abstract void createWithMalformedXml();
115
116     protected void setupCreateWithMalformedXml() {
117         clearSetup();
118         // Expected status code: 400 Bad Request
119         EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
120         REQUEST_TYPE = ServiceRequestType.CREATE;
121     }
122
123     @Override
124     public abstract void createWithWrongXmlSchema();
125
126     protected void setupCreateWithWrongXmlSchema() {
127         clearSetup();
128         // Expected status code: 400 Bad Request
129         EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
130         REQUEST_TYPE = ServiceRequestType.CREATE;
131     }
132
133
134     // ---------------------------------------------------------------
135     // CRUD tests : READ tests
136     // ---------------------------------------------------------------
137
138     // Success outcomes
139     
140     @Override
141     public abstract void read();
142
143     protected void setupRead() {
144         clearSetup();
145         // Expected status code: 200 OK
146         EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
147         REQUEST_TYPE = ServiceRequestType.READ;
148     }
149
150     // Failure outcomes
151
152     @Override
153     public abstract void readNonExistent();
154
155     protected void setupReadNonExistent() {
156         clearSetup();
157         // Expected status code: 404 Not Found
158         EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
159         REQUEST_TYPE = ServiceRequestType.READ;
160     }
161
162
163     // ---------------------------------------------------------------
164     // CRUD tests : READ (list, or multiple) tests
165     // ---------------------------------------------------------------
166
167     // Success outcomes
168
169     @Override
170     public abstract void readList();
171
172     protected void setupReadList() {
173         clearSetup();
174         // Expected status code: 200 OK
175         EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
176         REQUEST_TYPE = ServiceRequestType.READ_MULTIPLE;
177     }
178
179     // Failure outcomes
180
181     // None tested at present.
182
183
184     // ---------------------------------------------------------------
185     // CRUD tests : UPDATE tests
186     // ---------------------------------------------------------------
187
188     // Success outcomes
189     // ----------------
190
191     @Override
192     public abstract void update();
193
194     protected void setupUpdate() {
195         clearSetup();
196         // Expected status code: 200 OK
197         EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
198         REQUEST_TYPE = ServiceRequestType.UPDATE;
199     }
200
201     // Failure outcomes
202
203     @Override
204     public abstract void updateWithMalformedXml();
205
206     protected void setupUpdateWithMalformedXml() {
207         clearSetup();
208         // Expected status code: 400 Bad Request
209         EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
210         REQUEST_TYPE = ServiceRequestType.UPDATE;
211     }
212
213     @Override
214     public abstract void updateWithWrongXmlSchema();
215
216     protected void setupUpdateWithWrongXmlSchema() {
217         clearSetup();
218         // Expected status code: 400 Bad Request
219         EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
220         REQUEST_TYPE = ServiceRequestType.UPDATE;
221     }
222
223     @Override
224     public abstract void updateNonExistent();
225
226     protected void setupUpdateNonExistent() {
227         clearSetup();
228         // Expected status code: 404 Not Found
229         EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
230         REQUEST_TYPE = ServiceRequestType.UPDATE;
231     }
232
233
234     // ---------------------------------------------------------------
235     // CRUD tests : DELETE tests
236     // ---------------------------------------------------------------
237
238     // Success outcomes
239
240     @Override
241     public abstract void delete();
242     
243     protected void setupDelete() {
244         clearSetup();
245         // Expected status code: 200 OK
246         EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
247         REQUEST_TYPE = ServiceRequestType.DELETE;
248     }
249     
250     // Failure outcomes
251     
252     @Override
253     public abstract void deleteNonExistent();
254
255     protected void setupDeleteNonExistent() {
256         clearSetup();
257         // Expected status code: 404 Not Found
258         EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
259         REQUEST_TYPE = ServiceRequestType.DELETE;
260     }
261
262
263     // ---------------------------------------------------------------
264     // Abstract utility methods
265     //
266     // Must be implemented by classes that extend
267     // this abstract base class.
268     // ---------------------------------------------------------------
269
270     /**
271      * Returns the URL path component of the service.
272      *
273      * This component will follow directly after the
274      * base path, if any.
275      */
276     protected abstract String getServicePathComponent();
277
278
279     // ---------------------------------------------------------------
280     // Utility methods
281     // ---------------------------------------------------------------
282
283     /**
284      * Reinitializes setup values, to help expose any unintended reuse
285      * of those values between tests.
286      */
287     protected void clearSetup() {
288         EXPECTED_STATUS_CODE = 0;
289         REQUEST_TYPE = ServiceRequestType.NON_EXISTENT;
290     }
291
292     // @TODO Add Javadoc comments to all methods requiring them, below.
293
294     protected String invalidStatusCodeMessage(ServiceRequestType requestType, int statusCode) {
295         return 
296             "Status code '" + statusCode + "' in response is NOT within the expected set: " +
297             requestType.validStatusCodesAsString();
298     }
299     
300     protected String getServiceRootURL() {
301         return serviceClient.getBaseURL() + getServicePathComponent();
302     }
303
304     protected String getResourceURL(String resourceIdentifier) {
305         return getServiceRootURL() + "/" + resourceIdentifier;
306     }
307
308     protected int submitRequest(HttpMethod method) {
309      int statusCode = 0;
310         try {
311             statusCode = httpClient.executeMethod(method);
312         } catch(HttpException e) {
313             logger.error("Fatal protocol violation: ", e);
314         } catch(IOException e) {
315             logger.error("Fatal transport error: ", e);
316         } catch(Exception e) {
317             logger.error("Unknown exception: ", e);
318         } finally {
319             // Release the connection.
320             method.releaseConnection();
321         }
322         return statusCode;
323     }
324     
325     protected int submitRequest(EntityEnclosingMethod method, RequestEntity entity) {
326         int statusCode = 0;
327         try {
328             method.setRequestEntity(entity);
329             statusCode = httpClient.executeMethod(method);
330         } catch(HttpException e) {
331             logger.error("Fatal protocol violation: ", e);
332         } catch(IOException e) {
333             logger.error("Fatal transport error: ", e);
334         } catch(Exception e) {
335             logger.error("Unknown exception: ", e);
336         } finally {
337             // Release the connection.
338             method.releaseConnection();
339         }
340         return statusCode;
341     }
342     
343     protected StringRequestEntity getXmlEntity(String contents) {
344         if (contents == null) {
345             contents = "";
346         }
347         StringRequestEntity entity = null;
348         final String XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
349         final String XML_CONTENT_TYPE=MediaType.APPLICATION_XML;
350         final String UTF8_CHARSET_NAME = "UTF-8";
351         try {
352             entity =
353                 new StringRequestEntity(XML_DECLARATION + contents, XML_CONTENT_TYPE, UTF8_CHARSET_NAME);
354         } catch (UnsupportedEncodingException e) {
355             logger.error("Unsupported character encoding error: ", e);
356         }
357         return entity;
358     }
359
360     protected String extractId(ClientResponse<Response> res) {
361         MultivaluedMap mvm = res.getMetadata();
362         String uri = (String) ((ArrayList) mvm.get("Location")).get(0);
363         verbose("extractId:uri=" + uri);
364         String[] segments = uri.split("/");
365         String id = segments[segments.length - 1];
366         verbose("id=" + id);
367         return id;
368     }
369
370     protected void verbose(String msg) {
371         if (logger.isDebugEnabled()) {
372             logger.debug(msg);
373         }
374     }
375
376     protected void verbose(String msg, Object o, Class clazz) {
377         try{
378             verbose(msg);
379             JAXBContext jc = JAXBContext.newInstance(clazz);
380             Marshaller m = jc.createMarshaller();
381             m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
382                     Boolean.TRUE);
383             m.marshal(o, System.out);
384         }catch(Exception e){
385             e.printStackTrace();
386         }
387     }
388
389     protected void verboseMap(MultivaluedMap map) {
390         for(Object entry : map.entrySet()){
391             MultivaluedMap.Entry mentry = (MultivaluedMap.Entry) entry;
392             verbose("    name=" + mentry.getKey() + " value=" + mentry.getValue());
393         }
394     }
395
396     protected String createIdentifier() {
397         long identifier = System.currentTimeMillis();
398         return Long.toString(identifier);
399     }
400
401     protected String createNonExistentIdentifier() {
402         return Long.toString(Long.MAX_VALUE);
403     }
404     
405 }
406
407