]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
69e1ac6d0b737183675780f210f1099066995ded
[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  * https://source.collectionspace.org/collection-space/LICENSE.txt
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23  
24 package org.collectionspace.services.client.test;
25
26 import java.util.ArrayList;
27 import java.util.List;
28 import javax.ws.rs.core.MultivaluedMap;
29 import javax.ws.rs.core.Response;
30 import javax.xml.bind.JAXBContext;
31 import javax.xml.bind.Marshaller;
32 import org.jboss.resteasy.client.ClientResponse;
33 import org.testng.Assert;
34 import org.testng.annotations.Test;
35
36 import org.collectionspace.services.client.CollectionObjectClient;
37 import org.collectionspace.services.collectionobject.CollectionObject;
38 import org.collectionspace.services.collectionobject.CollectionObjectList;
39
40 import java.io.IOException;
41 import java.io.UnsupportedEncodingException;
42 import javax.ws.rs.core.MediaType;
43 import javax.ws.rs.core.Response.Status;
44 // import org.jboss.resteasy.client.ClientRequest;
45 import org.collectionspace.services.client.TestServiceClient;
46 import org.apache.commons.httpclient.Header;
47 import org.apache.commons.httpclient.HttpClient;
48 import org.apache.commons.httpclient.HttpException;
49 import org.apache.commons.httpclient.HttpMethod;
50 import org.apache.commons.httpclient.HttpStatus;
51 import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
52 import org.apache.commons.httpclient.methods.GetMethod;
53 import org.apache.commons.httpclient.methods.PostMethod;
54 import org.apache.commons.httpclient.methods.PutMethod;
55 import org.apache.commons.httpclient.methods.RequestEntity;
56 import org.apache.commons.httpclient.methods.StringRequestEntity;
57
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 /**
62  * CollectionObjectServiceTest, carries out tests against a
63  * deployed and running CollectionObject Service.
64  * 
65  * $LastChangedRevision$
66  * $LastChangedDate$
67  */
68 public class CollectionObjectServiceTest {
69
70   // Instance variables specific to this test.
71   final Logger logger = LoggerFactory.getLogger(CollectionObjectServiceTest.class);
72   private CollectionObjectClient client = new CollectionObjectClient();
73
74   // Instance variables common to all entity service test classes.
75   private String knownObjectId = null;
76   private final String NON_EXISTENT_ID = createNonExistentIdentifier();
77   private HttpClient httpClient = new HttpClient();
78   private TestServiceClient serviceClient = new TestServiceClient();
79
80   
81   // ---------------------------------------------------------------
82   // Service Discovery tests
83   // ---------------------------------------------------------------
84
85   // TBA
86   
87   
88   // ---------------------------------------------------------------
89   // CRUD tests : CREATE tests
90   // ---------------------------------------------------------------
91
92   // Success outcomes
93   // ----------------
94   
95   /**
96    * Tests creation of a new object of the specified type.
97    *
98    * The 'Location' header will contain the URL for the newly created object.
99    * This is required by the extractId() utility method, below.
100    *
101    * The newly-created object is also used by other test(s)
102    * (e.g. update, delete) which follow, below.
103    */
104   @Test
105   public void create() {
106
107     // Expected status code: 201 Created
108     final int EXPECTED_STATUS_CODE = Response.Status.CREATED.getStatusCode();
109
110     String identifier = this.createIdentifier();
111
112     CollectionObject collectionObject = createCollectionObject(identifier);
113     ClientResponse<Response> res = client.createCollectionObject(collectionObject);
114     verbose("create: status = " + res.getStatus());
115     Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE);
116
117     // Store the ID returned from this create operation for additional tests below.
118     knownObjectId = extractId(res);
119   }
120
121   /**
122    * Creates two or more new objects of the specified type.
123    *
124    * Repeatedly calls the create test, above, and relies on its
125    * test assertions.
126    *
127    * The newly-created objects are also used by other test(s)
128    * (e.g. read multiple/list) which follow, below.
129    */
130   @Test(dependsOnMethods = {"create"})
131   public void createMultiple() {
132     for(int i = 0; i < 3; i++){
133       this.create();
134     }
135   }
136
137   // Failure outcomes
138   // ----------------
139
140   /**
141    * Tests creation of an object of the specified type by sending a null to the client proxy.
142    *
143    */
144   @Test(dependsOnMethods = {"create"}, expectedExceptions = IllegalArgumentException.class)
145   public void createNull() {
146
147    // Expected result: IllegalArgumentException   
148     ClientResponse<Response> res = client.createCollectionObject(null);
149   }
150   
151   /**
152    * Tests the HttpClient-based code used to submit data, in various methods below.
153    */
154   @Test(dependsOnMethods = {"create", "read"})
155   public void testSubmitRequest() {
156
157     // Expected status code: 200 OK
158     final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
159
160     String url = getResourceURL(knownObjectId);
161     GetMethod method = new GetMethod(url);
162
163     int statusCode = submitRequest(method);
164     verbose("testSubmitRequest: url=" + url + " status=" + statusCode);
165     
166     // Evaluate the status code in the response.
167     Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE,
168       "expected " + EXPECTED_STATUS_CODE);
169
170   }
171                 
172   /**
173    * Tests creation of an object of the specified type by sending malformed XML data
174    * in the entity body of the request.
175    */
176 /*
177   @Test(dependsOnMethods = {"create", "testSubmitRequest"})
178   public void createWithMalformedXml() {
179
180     // Expected status code: 400 Bad Request
181     final int EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
182
183     // @TODO This test is currently commented out, because it returns a
184     // 500 Internal Server Error status code, rather than the expected status code.
185
186     String url = getServiceRootURL();
187     PostMethod method = new PostMethod(url);
188     // Note missing final angle bracket in text below.
189     final String MALFORMED_XML_DATA = "<malformed_xml>wrong schema contents</malformed_xml";
190     
191     // Prepare the entity body of the request.
192     StringRequestEntity entity = getXmlEntity(MALFORMED_XML_DATA);
193     
194     // Submit the request.
195     int statusCode = submitRequest(method, entity);
196     verbose("createWithMalformedXml url=" + url + " status=" + statusCode);
197     
198     // Evaluate the status code in the response.
199     Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE,
200       "expected " + EXPECTED_STATUS_CODE);
201   }
202 */
203
204   /**
205    * Tests creation of an object of the specified type by sending data
206    * in the wrong schema (e.g. in a format that doesn't match the object's schema)
207    * in the entity body of the request.
208     */
209 /*
210   @Test(dependsOnMethods = {"create", "testSubmitRequest", "createWithMalformedXml"})
211   public void createWithWrongSchema() {
212   
213     // Expected status code: 400 Bad Request
214     final int EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
215
216     // @TODO This test is currently commented out, because it returns a
217     // 500 Internal Server Error status code, rather than the expected status code.
218    
219     String url = getServiceRootURL();
220     PostMethod method = new PostMethod(url);
221     
222     // Prepare the entity body of the request.
223     final String WRONG_SCHEMA_DATA = "<wrong_schema>wrong schema contents</wrong_schema>";
224     StringRequestEntity entity = getXmlEntity(WRONG_SCHEMA_DATA);
225     
226     // Submit the request.
227     int statusCode = submitRequest(method, entity);
228     verbose("createWithWrongSchema url=" + url + " status=" + statusCode);
229     
230     // Evaluate the status code in the response.
231     Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE,
232       "expected " + EXPECTED_STATUS_CODE);
233   }
234   
235 */
236
237   /**
238    * Tests creation of an object of the specified type,
239    * by a user who is not authorized to perform this action.
240    */
241 /*
242   @Test(dependsOnMethods = {"create"})
243   public void createWithoutAuthorization() {
244
245     // Expected status code: 403 Forbidden
246     final int EXPECTED_STATUS_CODE = Response.Status.FORBIDDEN.getStatusCode();
247     
248     // @TODO Currently only a stub.  This test can be implemented
249     // when the service is revised to require authorization. 
250   }
251 */
252
253   /**
254    * Tests creation of a duplicate object of the specified type,
255    * whose unique resource identifier duplicates that of an existing object.
256    */
257 /*
258   @Test(dependsOnMethods = {"create"})
259   public void createDuplicate() {
260
261     // Expected status code: 409 Conflict
262     final int EXPECTED_STATUS_CODE = Response.Status.CONFLICT.getStatusCode();
263     
264     // @TODO This test is currently commented out because our current
265     // services do not appear to permit creation of duplicate records.
266     // Please see below for more details.
267     
268     // Note: there doesn't appear to be a way to create a duplicate
269     // resource (object) by POSTing:
270     //    
271     // 1. We can't POST to a specific resource by ID; that returns a
272     // response with a 405 Method Not Allowed status code.
273     //
274     // 2. If we POST to the container in which new resources are created,
275     // it doesn't appear that we can specify the CSID that the newly-created
276     // resource (object) will receive.
277     //
278     // If the two points above are accurate, this test is thus unneeded, until
279     // and unless, in our service(s), we begin detecting duplicates via a
280     // technique that isn't dependent on CSIDs; for instance, by checking for
281     // duplicate data in other information units (fields) whose values must be unique.
282     // 
283     // One possible example: checking for duplicate Accession numbers in
284     // the "Object entry" information unit.
285   }
286 */
287
288   // ---------------------------------------------------------------
289   // CRUD tests : READ tests
290   // ---------------------------------------------------------------
291
292   // Success outcomes
293   // ----------------
294   
295   /**
296    * Tests reading (i.e. retrieval) of an object of the specified type.
297    */
298   @Test(dependsOnMethods = {"create"})
299   public void read() {
300
301     // Expected status code: 200 OK
302     final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
303
304     ClientResponse<CollectionObject> res = 
305       client.getCollectionObject(knownObjectId);
306     verbose("read: status = " + res.getStatus());
307     Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE);
308   }
309
310   // Failure outcomes
311   // ----------------
312
313   /**
314    * Tests reading (i.e. retrieval) of an object of the specified type by a user who
315    * is not authorized to perform this action.
316    */
317 /*
318   @Test(dependsOnMethods = {"read"})
319   public void readWithoutAuthorization() {
320
321     // Expected status code: 403 Forbidden
322     final int EXPECTED_STATUS_CODE = Response.Status.FORBIDDEN.getStatusCode();
323     
324     // @TODO Currently only a stub.  This test can be implemented
325     // when the service is revised to require authorization. 
326   }
327 */
328
329   /**
330    * Tests reading (i.e. retrieval) of a non-existent object of the specified type,
331    * whose resource identifier does not exist at the specified URL.
332    */
333   @Test(dependsOnMethods = {"read"})
334   public void readNonExistent() {
335
336     // Expected status code: 404 Not Found
337     final int EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
338
339     ClientResponse<CollectionObject> res = 
340       client.getCollectionObject(NON_EXISTENT_ID);
341     verbose("readNonExistent: status = " + res.getStatus());
342     Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE);
343   }
344
345
346   // ---------------------------------------------------------------
347   // CRUD tests : READ (list, or multiple) tests
348   // ---------------------------------------------------------------
349
350   // Success outcomes
351   // ----------------
352
353   /**
354    * Tests reading (i.e. retrieval) of a list of multiple objects of the specified type.
355    *
356    * Also expected: The entity body in the response contains
357    * a representation of a list of objects of the specified type.
358    */
359   @Test(dependsOnMethods = {"createMultiple"})
360   public void readList() {
361
362     // Expected status code: 200 OK
363     final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
364   
365     // The 'read' method is expected to return at least an empty list.
366     ClientResponse<CollectionObjectList> res = client.getCollectionObjectList();
367     CollectionObjectList coList = res.getEntity();
368     verbose("readList: status = " + res.getStatus());
369     Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE);
370
371     if (logger.isDebugEnabled()) {
372       List<CollectionObjectList.CollectionObjectListItem> coItemList =
373         coList.getCollectionObjectListItem();
374       int i = 0;
375       for(CollectionObjectList.CollectionObjectListItem pli : coItemList){
376         verbose("readList: list-item[" + i + "] csid=" + pli.getCsid());
377         verbose("readList: list-item[" + i + "] objectNumber=" + pli.getObjectNumber());
378         verbose("readList: list-item[" + i + "] URI=" + pli.getUri());
379         i++;
380       }
381     }
382     
383   }
384
385   /**
386    * Tests reading (i.e. retrieval) of a list of multiple objects of the specified type
387    * when the contents of the list are expected to be empty.
388    *
389    * Also expected: The entity body in the response contains
390    * a representation of an empty list of objects of the specified type.
391    */
392 /*
393   @Test(dependsOnMethods = {"readList"})
394   public void readEmptyList() {
395
396     // Expected status code: 200 OK
397     // (NOTE: *not* 204 No Content)
398     final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
399
400     // @TODO Currently only a stub.
401   }
402 */
403   
404   // Failure outcomes
405   // ----------------
406
407   /**
408    * Tests reading (i.e. retrieval) of a list of objects of the specified type
409    * when sending unrecognized query parameters with the request.
410    */
411 /*
412   @Test(dependsOnMethods = {"readList"})
413   public void readListWithBadParams() {
414
415     // Expected status code: 400 Bad Request
416     final int EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
417
418     // @TODO This test is currently commented out, because it returns a
419     // 200 OK status code, rather than the expected status code.
420    
421     // @TODO Another variant of this test should use a URL for the service
422     // root that ends in a trailing slash.
423
424     String url = getServiceRootURL() + "?param=nonexistent";
425     GetMethod method = new GetMethod(url);
426     
427     // Submit the request.
428     int statusCode = submitRequest(method);
429     verbose("readListWithBadParams: url=" + url + " status=" + statusCode);
430     
431     // Evaluate the status code in the response.
432     Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE,
433       "expected " + EXPECTED_STATUS_CODE);
434   }
435 */
436
437   /**
438    * Tests reading (i.e. retrieval) of a list of objects of the specified type by a user who
439    * is not authorized to perform this action.
440    *
441    */
442 /*
443   @Test(dependsOnMethods = {"readList"})
444   public void readListWithoutAuthorization() {
445
446     // Expected status code: 403 Forbidden
447     final int EXPECTED_STATUS_CODE = Response.Status.FORBIDDEN.getStatusCode();
448
449     // @TODO Currently only a stub.  This test can be implemented
450     // when the service is revised to require authorization. 
451   }
452 */
453  
454
455   // ---------------------------------------------------------------
456   // CRUD tests : UPDATE tests
457   // ---------------------------------------------------------------
458
459   // Success outcomes
460   // ----------------
461
462   /**
463    * Tests updating the content of an object of the specified type.
464    *
465    * Also expected: The entity body in the response contains
466    * a representation of the updated object of the specified type.
467    */
468   @Test(dependsOnMethods = {"create"})
469   public void update() {
470
471     // Expected status code: 200 OK
472     final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
473
474     ClientResponse<CollectionObject> res = 
475       client.getCollectionObject(knownObjectId);
476     verbose("read: status = " + res.getStatus());
477     Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE);
478     CollectionObject collectionObject = res.getEntity();
479     verbose("Got object to update with ID: " + knownObjectId,
480         collectionObject, CollectionObject.class);
481
482     //collectionObject.setCsid("updated-" + knownObjectId);
483     collectionObject.setObjectNumber("updated-" + collectionObject.getObjectNumber());
484     collectionObject.setObjectName("updated-" + collectionObject.getObjectName());
485
486     // make call to update service
487     res = 
488       client.updateCollectionObject(knownObjectId, collectionObject);
489     verbose("update: status = " + res.getStatus());
490     Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE);
491     
492     // check the response
493     CollectionObject updatedCollectionObject = res.getEntity();
494     Assert.assertEquals(updatedCollectionObject.getObjectName(), 
495       collectionObject.getObjectName());
496     verbose("update: ", updatedCollectionObject, CollectionObject.class);
497   }
498
499   /**
500    * Tests updating the content of an object of the specified type
501    * by sending malformed XML data in the entity body of the request.
502    */
503 /*
504   @Test(dependsOnMethods = {"create", "testSubmitRequest"})
505   public void updateWithMalformedXml() {
506
507     // Expected status code: 400 Bad Request
508     final int EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
509
510     // @TODO This test is currently commented out, because it returns a
511     // 500 Internal Server Error status code, rather than the expected status code.
512
513     String url = getResourceURL(knownObjectId);
514     PutMethod method = new PutMethod(url);
515     
516     // Prepare the entity body of the request.
517     //
518     // Note missing final angle bracket in text below.
519     final String MALFORMED_XML_DATA = "<malformed_xml>wrong schema contents</malformed_xml";
520     StringRequestEntity entity = getXmlEntity(MALFORMED_XML_DATA);
521     
522     // Submit the request.
523     int statusCode = submitRequest(method, entity);
524     verbose("updateWithMalformedXml: url=" + url + " status=" + statusCode);
525     
526     // Evaluate the status code in the response.
527     Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE,
528       "expected " + EXPECTED_STATUS_CODE);
529   }
530 */
531
532   /**
533    * Tests updating the content of an object of the specified type
534    * by sending data in the wrong schema (e.g. in a format that
535    * doesn't match the object's schema) in the entity body of the request.
536    */
537 /*
538   @Test(dependsOnMethods = {"create", "testSubmitRequest", "createWithMalformedXml"})
539   public void updateWithWrongSchema() {
540   
541     // Expected status code: 400 Bad Request
542     final int EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
543     
544     // @TODO This test is currently commented out, because it returns a
545     // 500 Internal Server Error status code, rather than the expected status code.
546    
547     String url = getResourceURL(knownObjectId);
548     PutMethod method = new PutMethod(url);
549     
550     // Prepare the entity body of the request.
551     final String WRONG_SCHEMA_DATA = "<wrong_schema>wrong schema contents</wrong_schema>";
552     StringRequestEntity entity = getXmlEntity(WRONG_SCHEMA_DATA);
553     
554     // Submit the request.
555     int statusCode = submitRequest(method, entity);
556     verbose("updateWithWrongSchema: url=" + url + " status=" + statusCode);
557     
558     // Evaluate the status code in the response.
559     Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE,
560       "expected " + EXPECTED_STATUS_CODE);
561   }
562 */
563
564   /**
565    * Tests updating the content of an object of the specified type,
566    * by a user who is not authorized to perform this action.
567    */
568 /*
569   @Test(dependsOnMethods = {"update"})
570   public void updateWithoutAuthorization() {
571
572     // Expected status code: 403 Forbidden
573     final int EXPECTED_STATUS_CODE = Response.Status.FORBIDDEN.getStatusCode();
574
575     // @TODO Currently only a stub.  This test can be implemented
576     // when the service is revised to require authorization. 
577   }
578 */
579
580   /**
581    * Tests updating the content of a non-existent object of the specified type,
582    * whose resource identifier does not exist.
583    */
584   @Test(dependsOnMethods = {"update"})
585   public void updateNonExistent() {
586
587     // Expected status code: 404 Not Found
588     final int EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
589
590     // Note: The ID used in this 'create' call may be arbitrary.
591     // The only relevant ID may be the one used in updateCollectionObject(), below.
592     CollectionObject collectionObject = createCollectionObject(NON_EXISTENT_ID);
593     // make call to update service
594     ClientResponse<CollectionObject> res =
595       client.updateCollectionObject(NON_EXISTENT_ID, collectionObject);
596     verbose("updateNonExistent: status = " + res.getStatus());
597     Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE);
598   }
599
600
601   // ---------------------------------------------------------------
602   // CRUD tests : DELETE tests
603   // ---------------------------------------------------------------
604
605   // Success outcomes
606   // ----------------
607
608   /**
609    * Tests deleting an object of the specified type.
610    *
611    * Expected status code: 200 OK
612    */
613   @Test(dependsOnMethods = 
614     {"create", "read", "testSubmitRequest", "update"})
615   public void delete() {
616
617     // Expected status code: 200 OK
618     final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
619
620     ClientResponse<Response> res = client.deleteCollectionObject(knownObjectId);
621     verbose("delete: status = " + res.getStatus());
622     Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE);
623   }
624
625   // Failure outcomes
626   // ----------------
627
628   /**
629    * Tests deleting an object of the specified type,
630    * by a user who is not authorized to perform this action.
631    */
632 /*
633   @Test(dependsOnMethods = {"delete"})
634   public void deleteWithoutAuthorization() {
635
636     // Expected status code: 403 Forbidden
637     final int EXPECTED_STATUS_CODE = Response.Status.FORBIDDEN.getStatusCode();
638
639     // @TODO Currently only a stub.  This test can be implemented
640     // when the service is revised to require authorization. 
641   }
642 */
643
644   /**
645    * Tests deleting a non-existent object of the specified type,
646    * whose resource identifier does not exist at the specified URL.
647    */
648   @Test(dependsOnMethods = {"delete"})
649   public void deleteNonExistent() {
650
651     // Expected status code: 404 Not Found
652     final int EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
653
654     ClientResponse<Response> res =
655       client.deleteCollectionObject(NON_EXISTENT_ID);
656     verbose("deleteNonExistent: status = " + res.getStatus());
657     Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE);
658   }
659
660
661   // ---------------------------------------------------------------
662   // Utility methods used by tests above
663   // ---------------------------------------------------------------
664
665   // Methods specific to this test
666
667   private CollectionObject createCollectionObject(String identifier) {
668     CollectionObject collectionObject = createCollectionObject("objectNumber-" + identifier,
669         "objectName-" + identifier);
670     return collectionObject;
671   }
672
673   private CollectionObject createCollectionObject(String objectNumber, String objectName) {
674     CollectionObject collectionObject = new CollectionObject();
675     collectionObject.setObjectNumber(objectNumber);
676     collectionObject.setObjectName(objectName);
677     return collectionObject;
678   }
679   
680   private String getServicePathComponent() {
681     // @TODO Determine if it is possible to obtain this value programmatically.
682     // We set this in an annotation in the CollectionObjectProxy interface, for instance.
683     final String SERVICE_PATH_COMPONENT = "collectionobjects";
684     return SERVICE_PATH_COMPONENT;
685   }
686   
687   // Methods common to all entity service test classes.  These can be
688   // moved out of individual service test classes into a common class,
689   // perhaps at the top-level 'client' module.
690
691 /*
692 @TODO To be added:
693 Utility that asserts various HTTP status codes received for a given type of a request.
694 This should be used from any test we write for a service. For example, this utility
695 could take the following two params.
696 import javax.ws.rs.core.Response;
697 final static public boolean checkStatus(String method, Response.Status status);
698 Perhaps you could find about method used from org.jboss.resteasy.client.ClientResponse metadata.
699 */
700   
701   private String getServiceRootURL() {
702     return serviceClient.getBaseURL() + getServicePathComponent();
703   }
704
705   private String getResourceURL(String id) {
706     return getServiceRootURL() + "/" + id;
707   }
708
709   private int submitRequest(HttpMethod method) {
710    int statusCode = 0;
711     try {
712       statusCode = httpClient.executeMethod(method);
713     } catch(HttpException e) {
714       logger.error("Fatal protocol violation: ", e);
715     } catch(IOException e) {
716       logger.error("Fatal transport error: ", e);
717     } catch(Exception e) {
718       logger.error("Unknown exception: ", e);
719     } finally {
720       // Release the connection.
721       method.releaseConnection();
722     }
723     return statusCode;
724   }
725   
726   private int submitRequest(EntityEnclosingMethod method, RequestEntity entity) {
727     int statusCode = 0;
728     try {
729       method.setRequestEntity(entity);
730       statusCode = httpClient.executeMethod(method);
731     } catch(HttpException e) {
732       logger.error("Fatal protocol violation: ", e);
733     } catch(IOException e) {
734       logger.error("Fatal transport error: ", e);
735     } catch(Exception e) {
736       logger.error("Unknown exception: ", e);
737     } finally {
738       // Release the connection.
739       method.releaseConnection();
740     }
741     return statusCode;
742   }
743   
744   private StringRequestEntity getXmlEntity(String contents) {
745     if (contents == null) {
746       contents = "";
747     }
748     StringRequestEntity entity = null;
749     final String XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
750     final String XML_CONTENT_TYPE=MediaType.APPLICATION_XML;
751     final String UTF8_CHARSET_NAME = "UTF-8";
752     try {
753       entity =
754         new StringRequestEntity(XML_DECLARATION + contents, XML_CONTENT_TYPE, UTF8_CHARSET_NAME);
755     } catch (UnsupportedEncodingException e) {
756       logger.error("Unsupported character encoding error: ", e);
757     }
758     return entity;
759   }
760
761   private String extractId(ClientResponse<Response> res) {
762     MultivaluedMap mvm = res.getMetadata();
763     String uri = (String) ((ArrayList) mvm.get("Location")).get(0);
764     verbose("extractId:uri=" + uri);
765     String[] segments = uri.split("/");
766     String id = segments[segments.length - 1];
767     verbose("id=" + id);
768     return id;
769   }
770
771   private void verbose(String msg) {
772     if (logger.isDebugEnabled()) {
773       logger.debug(msg);
774     }
775   }
776
777   private void verbose(String msg, Object o, Class clazz) {
778     try{
779       verbose(msg);
780       JAXBContext jc = JAXBContext.newInstance(clazz);
781       Marshaller m = jc.createMarshaller();
782       m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
783           Boolean.TRUE);
784       m.marshal(o, System.out);
785     }catch(Exception e){
786       e.printStackTrace();
787     }
788   }
789
790   private void verboseMap(MultivaluedMap map) {
791     for(Object entry : map.entrySet()){
792       MultivaluedMap.Entry mentry = (MultivaluedMap.Entry) entry;
793       verbose("  name=" + mentry.getKey() + " value=" + mentry.getValue());
794     }
795   }
796
797   private String createIdentifier() {
798     long identifier = System.currentTimeMillis();
799     return Long.toString(identifier);
800   }
801
802   private String createNonExistentIdentifier() {
803     return Long.toString(Long.MAX_VALUE);
804   }
805   
806 }