39d28dd7228c31883fdf8ae6052b52bfd88ea6cf
[ui/dmaapbc.git] /
1 /*-
2  * ================================================================================
3  * DCAE DMaaP Bus Controller REST Client
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * 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 package org.openecomp.dcae.dmaapbc.client;
21
22 import java.net.URI;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import org.apache.http.client.utils.URIBuilder;
27 import org.openecomp.dcae.dmaapbc.model.DR_Node;
28 import org.openecomp.dcae.dmaapbc.model.DR_Pub;
29 import org.openecomp.dcae.dmaapbc.model.DR_Sub;
30 import org.openecomp.dcae.dmaapbc.model.DcaeLocation;
31 import org.openecomp.dcae.dmaapbc.model.Dmaap;
32 import org.openecomp.dcae.dmaapbc.model.DmaapObject;
33 import org.openecomp.dcae.dmaapbc.model.ErrorResponse;
34 import org.openecomp.dcae.dmaapbc.model.Feed;
35 import org.openecomp.dcae.dmaapbc.model.MR_Client;
36 import org.openecomp.dcae.dmaapbc.model.MR_Cluster;
37 import org.openecomp.dcae.dmaapbc.model.Topic;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 import com.fasterxml.jackson.annotation.JsonInclude;
42 import com.fasterxml.jackson.core.type.TypeReference;
43 import com.fasterxml.jackson.databind.DeserializationFeature;
44 import com.fasterxml.jackson.databind.ObjectMapper;
45
46 /**
47  * Provides methods to communicate with the DMaaP Bus Controller REST API. This
48  * hides all JSON; instead it accepts and returns Java objects.
49  */
50 public class DmaapBcRestClient extends SimpleRestClientBase {
51
52         private static Logger logger = LoggerFactory.getLogger(DmaapBcRestClient.class);
53
54         // Omit leading and trailing slashes here
55         private static final String DCAELOCATIONS = "dcaeLocations";
56         private static final String DMAAP = "dmaap";
57         private static final String DR_NODES = "dr_nodes";
58         private static final String DR_PUBS = "dr_pubs";
59         private static final String DR_SUBS = "dr_subs";
60         private static final String FEEDS = "feeds";
61         private static final String TOPICS = "topics";
62         private static final String MR_CLUSTERS = "mr_clusters";
63         private static final String MR_CLIENTS = "mr_clients";
64
65         /**
66          * Reusable JSON (de)serializer
67          */
68         private final ObjectMapper mapper;
69
70         /**
71          * URL of the DMAAP REST endpoint
72          */
73         private final String dmaapRestUrl;
74
75         /**
76          * Constructor that configures the client for the specified endpoint using
77          * no authentication.
78          * 
79          * @param dmaapRestUrl
80          *            URL of the endpoint
81          */
82         public DmaapBcRestClient(final String dmaapRestUrl) {
83                 super();
84                 this.dmaapRestUrl = dmaapRestUrl;
85                 this.mapper = new ObjectMapper();
86                 // Don't serialize null-value fields in objects
87                 this.mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
88         }
89
90         /**
91          * Constructor that onfigures the client for the specified endpoint using
92          * the specified username and password for basic HTTP authentication.
93          * 
94          * @param dmaapRestUrl
95          *            URL of the endpoint
96          * @param username
97          *            User name
98          * @param password
99          *            Password
100          */
101         public DmaapBcRestClient(final String dmaapRestUrl, final String username, final String password) {
102                 super(username, password);
103                 this.dmaapRestUrl = dmaapRestUrl;
104                 this.mapper = new ObjectMapper();
105                 // Don't serialize null-value fields in objects
106                 this.mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
107         }
108
109         /**
110          * Configures the behavior of the JSON deserializer used to build business
111          * objects (e.g., a Feed) from REST responses.
112          * 
113          * @param failOnUnknownProperties
114          *            If true, rejects JSON responses with unexpected fields
115          *            (default behavior); if false, ignores unexpected fields.
116          */
117         public void setFailOnUnknownProperties(boolean failOnUnknownProperties) {
118                 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
119         }
120
121         /**
122          * Gets the DMaaP endpoint URL that is used by methods in this class.
123          * 
124          * @return dmaapEndpointUrl
125          */
126         public String getDmaapRestUrl() {
127                 return this.dmaapRestUrl;
128         }
129
130         /////////////////////////////////////////////////////////////////////
131
132         /**
133          * Gets a list of DCAE locations.
134          * 
135          * @return List of DmaapObject: list contains DcaeLocation object(s) on
136          *         success; a single ErrorResponse object if the remote site rejects
137          *         the request.
138          * @throws Exception
139          *             if host cannot be reached, response cannot be parsed, etc.
140          */
141         public List<DmaapObject> getDcaeLocations() throws Exception {
142                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(DCAELOCATIONS));
143                 logger.debug("getDcaeLocations: resp is {}", hsr);
144                 if (hsr == null)
145                         throw new Exception("getDcaeLocations: unexpected null response");
146
147                 List<DmaapObject> responseList = null;
148                 try {
149                         TypeReference<List<DcaeLocation>> typeRef = new TypeReference<List<DcaeLocation>>() {
150                         };
151                         responseList = mapper.readValue(hsr.getResponseString(), typeRef);
152                 } catch (Exception ex) {
153                         logger.debug("getDcaeLocations: trying to parse response as error: {}", ex.toString());
154                         // If this parse fails, let the exception be thrown
155                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
156                         responseList = new ArrayList<DmaapObject>();
157                         responseList.add(errResp);
158                 }
159                 return responseList;
160         }
161
162         /**
163          * Gets the DCAE location with the specified name.
164          * 
165          * @param locName
166          *            name of the location to get
167          * @return DmaapObject: a DcaeLocation object on success; an ErrorResponse
168          *         object if the remote site rejects the request.
169          * @throws Exception
170          *             if host cannot be reached, response cannot be parsed, etc.
171          */
172         public DmaapObject getDcaeLocation(final String locName) throws Exception {
173                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(DCAELOCATIONS, locName));
174                 logger.debug("getDcaeLocation: resp is {}", hsr);
175                 if (hsr == null)
176                         throw new Exception("getDcaeLocation: unexpected null response");
177
178                 DmaapObject response = null;
179                 try {
180                         response = mapper.readValue(hsr.getResponseString(), DcaeLocation.class);
181                 } catch (Exception ex) {
182                         logger.debug("getDcaeLocation: trying to parse response as error: {}", ex.toString());
183                         // If this parse fails, let the exception be thrown
184                         response = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
185                 }
186                 return response;
187         }
188
189         /**
190          * Creates a DCAE location in DMaaP.
191          * 
192          * @param dcaeLoc
193          *            DcaeLocation to be created
194          * @return Status and response: expect 200 and a DcaeLocation on success; a
195          *         code and an ErrorResponse on failure.
196          * @throws Exception
197          *             if host cannot be reached, response cannot be parsed, etc.
198          */
199         public HttpStatusAndResponse<Object> postDcaeLocation(DcaeLocation dcaeLoc) throws Exception {
200                 String jsonBody = mapper.writeValueAsString(dcaeLoc);
201                 HttpStatusAndResponse<String> hsr = postRestContent(buildDmaapUri(DCAELOCATIONS), jsonBody);
202                 if (hsr == null)
203                         throw new Exception("postDcaeLocation: unexpected null response");
204                 logger.debug("postDcaeLocation: resp is {}", hsr);
205                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
206                 if (hsr.getResponse() == null) {
207                         logger.debug("postDcaeLocation: null response body");
208                         return response;
209                 }
210                 try {
211                         DcaeLocation resp = mapper.readValue(hsr.getResponseString(), DcaeLocation.class);
212                         response.setResponse(resp);
213                 } catch (Exception ex) {
214                         logger.debug("postDcaeLocation: trying to parse response as error: {}", ex.toString());
215                         // If this parse fails, let the exception be thrown
216                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
217                         response.setResponse(errResp);
218                 }
219                 return response;
220         }
221
222         /**
223          * Deletes the DCAE location with the specified name.
224          * 
225          * @param locName
226          *            Name of the location to delete
227          * @return Status and response: expect 204 and a DcaeLocation on success; a
228          *         code and an ErrorResponse on failure.
229          * @throws Exception
230          *             if host cannot be reached, response cannot be parsed, etc.
231          */
232         public HttpStatusAndResponse<Object> deleteDcaeLocation(final String locName) throws Exception {
233                 HttpStatusAndResponse<String> hsr = deleteRestContent(buildDmaapUri(DCAELOCATIONS, locName));
234                 logger.debug("deleteDcaeLocation: resp is {}", hsr);
235                 if (hsr == null)
236                         throw new Exception("deleteDcaeLocation: unexpected null response");
237                 // Returns a loc on success, error message string on error.
238                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
239                 if (hsr.getResponse() == null) {
240                         logger.debug("deleteDcaeLocation: null response body");
241                         return response;
242                 }
243                 try {
244                         DcaeLocation resp = mapper.readValue(hsr.getResponseString(), DcaeLocation.class);
245                         response.setResponse(resp);
246                 } catch (Exception ex) {
247                         logger.debug("deleteDcaeLocation: trying to parse response as error: {}", ex.toString());
248                         // If this parse fails, let the exception be thrown
249                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
250                         response.setResponse(errResp);
251                 }
252                 return response;
253         }
254
255         /**
256          * Updates a DCAE location.
257          * 
258          * @param dcaeLoc
259          *            DCAE Location to be updated
260          * @return Status and response; expect 200 and a DcaeLocation on success, a
261          *         string error on failure.
262          * @throws Exception
263          *             if host cannot be reached, response cannot be parsed, etc.
264          */
265         public HttpStatusAndResponse<Object> putDcaeLocation(DcaeLocation dcaeLoc) throws Exception {
266                 String jsonBody = mapper.writeValueAsString(dcaeLoc);
267                 HttpStatusAndResponse<String> hsr = putRestContent(buildDmaapUri(DCAELOCATIONS, dcaeLoc.getDcaeLocationName()),
268                                 jsonBody);
269                 logger.debug("putDcaeLocation: resp is {}", hsr);
270                 if (hsr == null)
271                         throw new Exception("putDcaeLocation: unexpected null response");
272                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
273                 if (hsr.getResponse() == null) {
274                         logger.debug("putDcaeLocation: null response body");
275                         return response;
276                 }
277                 try {
278                         DcaeLocation resp = mapper.readValue(hsr.getResponseString(), DcaeLocation.class);
279                         response.setResponse(resp);
280                 } catch (Exception ex) {
281                         logger.debug("putDcaeLocation: trying to parse response as error: {}", ex.toString());
282                         // If this parse fails, let the exception be thrown
283                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
284                         response.setResponse(errResp);
285                 }
286                 return response;
287         }
288
289         /////////////////////////////////////////////////////////////////////
290
291         /**
292          * Gets the DMAAP instance for this DCAE deployment.
293          * 
294          * @return DmaapObject: a Dmaap object on success; an ErrorResponse object
295          *         if the remote site rejects the request.
296          * @throws Exception
297          *             if host cannot be reached, response cannot be parsed, etc.
298          */
299         public DmaapObject getDmaap() throws Exception {
300                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(DMAAP));
301                 logger.debug("getDmaap: resp is {}", hsr);
302                 if (hsr == null)
303                         throw new Exception("getDmaap: unexpected null response");
304                 DmaapObject response = null;
305                 try {
306                         response = mapper.readValue(hsr.getResponseString(), Dmaap.class);
307                 } catch (Exception ex) {
308                         logger.debug("getDmaap: trying to parse response as error: {}", ex.toString());
309                         // If this parse fails, let the exception be thrown
310                         response = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
311                 }
312                 return response;
313         }
314
315         /**
316          * Creates a new DMaaP set system wide configuration settings for the
317          * dcaeEnvironment
318          * 
319          * @param dmaap
320          *            Dmaap properties
321          * @return Status and response: expect 200 and a Dmaap on success; a code
322          *         and an ErrorResponse on failure.
323          * @throws Exception
324          *             if host cannot be reached, response cannot be parsed, etc.
325          */
326         public HttpStatusAndResponse<Object> postDmaap(Dmaap dmaap) throws Exception {
327                 String jsonBody = mapper.writeValueAsString(dmaap);
328                 HttpStatusAndResponse<String> hsr = postRestContent(buildDmaapUri(DMAAP), jsonBody);
329                 if (hsr == null)
330                         throw new Exception("postDmaap: unexpected null response");
331                 logger.debug("postDmaap: resp is {}", hsr);
332                 // Returns ? on success, error message string on error.
333                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
334                 if (hsr.getResponse() == null) {
335                         logger.debug("postDmaap: null response body");
336                         return response;
337                 }
338                 try {
339                         Dmaap resp = mapper.readValue(hsr.getResponseString(), Dmaap.class);
340                         response.setResponse(resp);
341                 } catch (Exception ex) {
342                         logger.debug("postDmaap: trying to parse response as error: {}", ex.toString());
343                         // If this parse fails, let the exception be thrown
344                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
345                         response.setResponse(errResp);
346                 }
347                 return response;
348         }
349
350         /**
351          * Updates DMaaP system wide configuration settings for the dcaeEnvironment.
352          * 
353          * @param dmaap
354          *            Dmaap properties
355          * @return Status and response; expect 200 and a DR_Pub on success; a code
356          *         and and ErrorResponse on failure.
357          * @throws Exception
358          *             if host cannot be reached, response cannot be parsed, etc.
359          */
360         public HttpStatusAndResponse<Object> putDmaap(Dmaap dmaap) throws Exception {
361                 String jsonBody = mapper.writeValueAsString(dmaap);
362                 // Oddly, this PUT has no ID parameter in the URL
363                 HttpStatusAndResponse<String> hsr = putRestContent(buildDmaapUri(DMAAP), jsonBody);
364                 if (hsr == null)
365                         throw new Exception("putDmaap: unexpected null response");
366                 logger.debug("putDmaap: resp is {}", hsr);
367                 // Returns ? on success, error message string on error.
368                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
369                 if (hsr.getResponse() == null) {
370                         logger.debug("putDmaap: null response body");
371                         return response;
372                 }
373                 try {
374                         Dmaap resp = mapper.readValue(hsr.getResponseString(), Dmaap.class);
375                         response.setResponse(resp);
376                 } catch (Exception ex) {
377                         logger.debug("putDmaap: trying to parse response as error: {}", ex.toString());
378                         // If this parse fails, let the exception be thrown
379                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
380                         response.setResponse(errResp);
381                 }
382                 return response;
383         }
384
385         // NO DELETE_DMAAP METHOD
386
387         /////////////////////////////////////////////////////////////////////
388
389         /**
390          * Gets a list of data router nodes.
391          * 
392          * @return List of DmaapObject: list contains DR_Node object(s) on success;
393          *         a single ErrorResponse object if the remote site rejects the
394          *         request.
395          * @throws Exception
396          *             if host cannot be reached, response cannot be parsed, etc.
397          */
398         public List<DmaapObject> getDRNodes() throws Exception {
399                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(DR_NODES));
400                 logger.debug("getDRNodes: resp is {}", hsr);
401                 if (hsr == null)
402                         throw new Exception("getDRNodes: unexpected null response");
403
404                 List<DmaapObject> responseList = null;
405                 try {
406                         TypeReference<List<DR_Node>> typeRef = new TypeReference<List<DR_Node>>() {
407                         };
408                         responseList = mapper.readValue(hsr.getResponseString(), typeRef);
409                 } catch (Exception ex) {
410                         logger.debug("getDRNodes: trying to parse response as error: {}", ex.toString());
411                         // If this parse fails, let the exception be thrown
412                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
413                         responseList = new ArrayList<DmaapObject>();
414                         responseList.add(errResp);
415                 }
416                 return responseList;
417         }
418
419         /**
420          * Gets a data router node with the specified ID.
421          * 
422          * @param fqdn
423          *            Name of the node to get
424          * @return DmaapObject: a DR_Node object on success; an ErrorResponse object
425          *         if the remote site rejects the request.
426          * @throws Exception
427          *             if host cannot be reached, response cannot be parsed, etc.
428          */
429         public DmaapObject getDRNode(final String fqdn) throws Exception {
430                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(DR_NODES, fqdn));
431                 logger.debug("getDRNode: resp is {}", hsr);
432                 if (hsr == null)
433                         throw new Exception("getDRNode: unexpected null response");
434                 DmaapObject response = null;
435                 try {
436                         response = mapper.readValue(hsr.getResponseString(), DR_Node.class);
437                 } catch (Exception ex) {
438                         logger.debug("getDRNode: trying to parse response as error: {}", ex.toString());
439                         // If this parse fails, let the exception be thrown
440                         response = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
441                 }
442                 return response;
443         }
444
445         /**
446          * Creates a data router node.
447          * 
448          * @param drNode
449          *            Node to be created
450          * @return Status and response: expect 200 and a DR_Node on success; a code
451          *         and an ErrorResponse on failure.
452          * @throws Exception
453          *             if host cannot be reached, response cannot be parsed, etc.
454          */
455         public HttpStatusAndResponse<Object> postDRNode(DR_Node drNode) throws Exception {
456                 String jsonBody = mapper.writeValueAsString(drNode);
457                 HttpStatusAndResponse<String> hsr = postRestContent(buildDmaapUri(DR_NODES), jsonBody);
458                 logger.debug("postDRNode: resp is {}", hsr);
459                 if (hsr == null)
460                         throw new Exception("postDRNode: unexpected null response");
461                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
462                 if (hsr.getResponse() == null) {
463                         logger.debug("postDRNode: null response body");
464                         return response;
465                 }
466                 try {
467                         DR_Node resp = mapper.readValue(hsr.getResponseString(), DR_Node.class);
468                         response.setResponse(resp);
469                 } catch (Exception ex) {
470                         logger.debug("postDRNode: trying to parse response as error: {}", ex.toString());
471                         // If this parse fails, let the exception be thrown
472                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
473                         response.setResponse(errResp);
474                 }
475                 return response;
476         }
477
478         /**
479          * Updates a data router node.
480          * 
481          * @param drNode
482          *            Node to be updated
483          * @return Status and response: expect 200 and a DR_Node on success; a code
484          *         and an ErrorResponse on failure.
485          * @throws Exception
486          *             if host cannot be reached, response cannot be parsed, etc.
487          */
488         public HttpStatusAndResponse<Object> putDRNode(DR_Node drNode) throws Exception {
489                 String jsonBody = mapper.writeValueAsString(drNode);
490                 HttpStatusAndResponse<String> hsr = putRestContent(buildDmaapUri(DR_NODES, drNode.getFqdn()), jsonBody);
491                 logger.debug("putDRNode: resp is {}", hsr);
492                 if (hsr == null)
493                         throw new Exception("putDRNode: unexpected null response");
494                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
495                 if (hsr.getResponse() == null) {
496                         logger.debug("putDRNode: null response body");
497                         return response;
498                 }
499                 try {
500                         DR_Node resp = mapper.readValue(hsr.getResponseString(), DR_Node.class);
501                         response.setResponse(resp);
502                 } catch (Exception ex) {
503                         logger.debug("postDRNode: trying to parse response as error: {}", ex.toString());
504                         // If this parse fails, let the exception be thrown
505                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
506                         response.setResponse(errResp);
507                 }
508                 return response;
509         }
510
511         /**
512          * Deletes the data router node with the specified FQDN.
513          * 
514          * @param fqdn
515          *            Name of the node to delete
516          * @return Status and response: expect 204 and a null on success; a code and
517          *         an ErrorResponse on failure.
518          * @throws Exception
519          *             if host cannot be reached, response cannot be parsed, etc.
520          */
521         public HttpStatusAndResponse<Object> deleteDRNode(final String fqdn) throws Exception {
522                 HttpStatusAndResponse<String> hsr = deleteRestContent(buildDmaapUri(DR_NODES, fqdn));
523                 logger.debug("deleteDRNode: resp is {}", hsr);
524                 if (hsr == null)
525                         throw new Exception("deleteDRNode: unexpected null response");
526                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
527                 if (hsr.getResponse() == null) {
528                         logger.debug("deleteDRNode: null response body");
529                         return response;
530                 }
531                 try {
532                         DR_Node resp = mapper.readValue(hsr.getResponseString(), DR_Node.class);
533                         response.setResponse(resp);
534                 } catch (Exception ex) {
535                         logger.debug("deleteDRNode: trying to parse response as error: {}", ex.toString());
536                         // If this parse fails, let the exception be thrown
537                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
538                         response.setResponse(errResp);
539                 }
540                 return response;
541         }
542
543         /////////////////////////////////////////////////////////////////////
544
545         /**
546          * Gets a list of data router publishers.
547          * 
548          * @return List of DmaapObject: list contains DR_Pub object(s) on success; a
549          *         single ErrorResponse object if the remote site rejects the
550          *         request.
551          * @throws Exception
552          *             if host cannot be reached, response cannot be parsed, etc.
553          */
554         public List<DmaapObject> getDRPubs() throws Exception {
555                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(DR_PUBS));
556                 logger.debug("getDRPubs: resp is {}", hsr);
557                 if (hsr == null)
558                         throw new Exception("getDRPubs: unexpected null response");
559                 List<DmaapObject> responseList = null;
560                 try {
561                         TypeReference<List<DR_Pub>> typeRef = new TypeReference<List<DR_Pub>>() {
562                         };
563                         responseList = mapper.readValue(hsr.getResponseString(), typeRef);
564                 } catch (Exception ex) {
565                         logger.debug("getDRPubs: trying to parse response as error: {}", ex.toString());
566                         // If this parse fails, let the exception be thrown
567                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
568                         responseList = new ArrayList<DmaapObject>();
569                         responseList.add(errResp);
570                 }
571                 return responseList;
572         }
573
574         /**
575          * Gets a data router publisher with the specified ID.
576          * 
577          * @param pubId
578          *            ID of the publisher to get
579          * @return DmaapObject: a DR_Pub object on success; an ErrorResponse object
580          *         if the remote site rejects the request.
581          * @throws Exception
582          *             if host cannot be reached, response cannot be parsed, etc.
583          */
584         public DmaapObject getDRPub(final String pubId) throws Exception {
585                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(DR_PUBS, pubId));
586                 logger.debug("getDRPub: resp is {}", hsr);
587                 if (hsr == null)
588                         throw new Exception("getDRPub: unexpected null response");
589                 DmaapObject response = null;
590                 try {
591                         response = mapper.readValue(hsr.getResponseString(), DR_Pub.class);
592                 } catch (Exception ex) {
593                         logger.debug("getDRPub: trying to parse response as error: {}", ex.toString());
594                         // If this parse fails, let the exception be thrown
595                         response = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
596                 }
597                 return response;
598         }
599
600         /**
601          * Creates a data router publisher.
602          * 
603          * @param drPub
604          *            Data router publisher properties
605          * @return Status and response: expect 200 and a DR_Pub on success; a code
606          *         and an ErrorResponse on failure.
607          * @throws Exception
608          *             if host cannot be reached, response cannot be parsed, etc.
609          */
610         public HttpStatusAndResponse<Object> postDRPub(DR_Pub drPub) throws Exception {
611                 String jsonBody = mapper.writeValueAsString(drPub);
612                 HttpStatusAndResponse<String> hsr = postRestContent(buildDmaapUri(DR_PUBS), jsonBody);
613                 logger.debug("postDRPub: resp is {}", hsr);
614                 if (hsr == null)
615                         throw new Exception("postDRPub: unexpected null response");
616                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
617                 if (hsr.getResponse() == null) {
618                         logger.debug("postDRPub: null response body");
619                         return response;
620                 }
621                 try {
622                         DR_Pub resp = mapper.readValue(hsr.getResponseString(), DR_Pub.class);
623                         response.setResponse(resp);
624                 } catch (Exception ex) {
625                         logger.debug("postDRPub: trying to parse response as error: {}", ex.toString());
626                         // If this parse fails, let the exception be thrown
627                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
628                         response.setResponse(errResp);
629                 }
630                 return response;
631         }
632
633         /**
634          * Updates a data router publisher.
635          * 
636          * @param drPub
637          *            Publisher to be updated
638          * @return Status and response: expect 200 and a DR_Pub on success, a code
639          *         and an ErrorResponse on failure.
640          * @throws Exception
641          *             if host cannot be reached, response cannot be parsed, etc.
642          */
643         public HttpStatusAndResponse<Object> putDRPub(DR_Pub drPub) throws Exception {
644                 String jsonBody = mapper.writeValueAsString(drPub);
645                 HttpStatusAndResponse<String> hsr = putRestContent(buildDmaapUri(DR_PUBS, drPub.getPubId()), jsonBody);
646                 logger.debug("putDRPub: resp is {}", hsr);
647                 if (hsr == null)
648                         throw new Exception("putDRPub: unexpected null response");
649                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
650                 if (hsr.getResponse() == null) {
651                         logger.debug("putDRPub: null response body");
652                         return response;
653                 }
654                 try {
655                         DR_Pub resp = mapper.readValue(hsr.getResponseString(), DR_Pub.class);
656                         response.setResponse(resp);
657                 } catch (Exception ex) {
658                         logger.debug("postDRPub: trying to parse response as error: {}", ex.toString());
659                         // If this parse fails, let the exception be thrown
660                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
661                         response.setResponse(errResp);
662                 }
663                 return response;
664         }
665
666         /**
667          * Deletes the data router publisher with the specified ID.
668          * 
669          * @param pubId
670          *            ID of the publisher to delete
671          * @return Status and response: expect 204 and a null on success; a code and
672          *         an ErrorResponse on failure.
673          * @throws Exception
674          *             if host cannot be reached, response cannot be parsed, etc.
675          */
676         public HttpStatusAndResponse<Object> deleteDRPub(final String pubId) throws Exception {
677                 HttpStatusAndResponse<String> hsr = deleteRestContent(buildDmaapUri(DR_PUBS, pubId));
678                 logger.debug("deleteDRPub: resp is {}", hsr);
679                 if (hsr == null)
680                         throw new Exception("deleteDRPub: unexpected null response");
681                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
682                 if (hsr.getResponse() == null) {
683                         logger.debug("deleteDRPub: null response body");
684                         return response;
685                 }
686                 try {
687                         DR_Pub resp = mapper.readValue(hsr.getResponseString(), DR_Pub.class);
688                         response.setResponse(resp);
689                 } catch (Exception ex) {
690                         logger.debug("deleteDRPub: trying to parse response as error: {}", ex.toString());
691                         // If this parse fails, let the exception be thrown
692                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
693                         response.setResponse(errResp);
694                 }
695                 return response;
696         }
697
698         /////////////////////////////////////////////////////////////////////
699
700         /**
701          * Gets a list of data router subscribers.
702          * 
703          * @return List of DmaapObject: list contains DR_Sub object(s) on success; a
704          *         single ErrorResponse object if the remote site rejects the
705          *         request.
706          * @throws Exception
707          *             if host cannot be reached, response cannot be parsed, etc.
708          */
709         public List<DmaapObject> getDRSubs() throws Exception {
710                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(DR_SUBS));
711                 logger.debug("getDRSubs: resp is {}", hsr);
712                 if (hsr == null)
713                         throw new Exception("getDRSubs: unexpected null response");
714                 List<DmaapObject> responseList = null;
715                 try {
716                         TypeReference<List<DR_Sub>> typeRef = new TypeReference<List<DR_Sub>>() {
717                         };
718                         responseList = mapper.readValue(hsr.getResponseString(), typeRef);
719                 } catch (Exception ex) {
720                         logger.debug("getDRSubs: trying to parse response as error: {}", ex.toString());
721                         // If this parse fails, let the exception be thrown
722                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
723                         responseList = new ArrayList<DmaapObject>();
724                         responseList.add(errResp);
725                 }
726                 return responseList;
727         }
728
729         /**
730          * Gets a data router subscriber with the specified ID.
731          * 
732          * @param subId
733          *            ID of the subscriber to get
734          * @return DmaapObject: a DR_Sub object on success; an ErrorResponse object
735          *         if the remote site rejects the request.
736          * @throws Exception
737          *             if host cannot be reached, response cannot be parsed, etc.
738          */
739         public DmaapObject getDRSub(final String subId) throws Exception {
740                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(DR_SUBS, subId));
741                 logger.debug("getDRPub: resp is {}", hsr);
742                 if (hsr == null)
743                         throw new Exception("getDRSub: unexpected null response");
744                 DmaapObject response = null;
745                 try {
746                         response = mapper.readValue(hsr.getResponseString(), DR_Sub.class);
747                 } catch (Exception ex) {
748                         logger.debug("getDRSub: trying to parse response as error: {}", ex.toString());
749                         // If this parse fails, let the exception be thrown
750                         response = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
751                 }
752                 return response;
753         }
754
755         /**
756          * Creates a data router subscriber.
757          * 
758          * @param drSub
759          *            Data router subscriber properties
760          * @return Status and response: expect 200 and a DR_Sub on success; a code
761          *         and an ErrorResponse on failure.
762          * @throws Exception
763          *             if host cannot be reached, response cannot be parsed, etc.
764          */
765         public HttpStatusAndResponse<Object> postDRSub(DR_Sub drSub) throws Exception {
766                 String jsonBody = mapper.writeValueAsString(drSub);
767                 HttpStatusAndResponse<String> hsr = postRestContent(buildDmaapUri(DR_SUBS), jsonBody);
768                 logger.debug("postDRSub: resp is {}", hsr);
769                 if (hsr == null)
770                         throw new Exception("postDRSub: unexpected null response");
771                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
772                 if (hsr.getResponse() == null) {
773                         logger.debug("postDRSub: null response body");
774                         return response;
775                 }
776                 try {
777                         DR_Sub resp = mapper.readValue(hsr.getResponseString(), DR_Sub.class);
778                         response.setResponse(resp);
779                 } catch (Exception ex) {
780                         logger.debug("postDRSub: trying to parse response as error: {}", ex.toString());
781                         // If this parse fails, let the exception be thrown
782                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
783                         response.setResponse(errResp);
784                 }
785                 return response;
786         }
787
788         /**
789          * Updates a data router subscriber.
790          * 
791          * @param drSub
792          *            Subscriber to be updated
793          * @return Status and response; expect 200 and a DR_Sub on success, a string
794          *         error on failure.
795          * @throws Exception
796          *             if host cannot be reached, response cannot be parsed, etc.
797          */
798         public HttpStatusAndResponse<Object> putDRSub(DR_Sub drSub) throws Exception {
799                 String jsonBody = mapper.writeValueAsString(drSub);
800                 HttpStatusAndResponse<String> hsr = putRestContent(buildDmaapUri(DR_SUBS, drSub.getSubId()), jsonBody);
801                 logger.debug("putDRSub: resp is {}", hsr);
802                 if (hsr == null)
803                         throw new Exception("putDRSub: unexpected null response");
804                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
805                 if (hsr.getResponse() == null) {
806                         logger.debug("putDRSub: null response body");
807                         return response;
808                 }
809                 try {
810                         DR_Sub resp = mapper.readValue(hsr.getResponseString(), DR_Sub.class);
811                         response.setResponse(resp);
812                 } catch (Exception ex) {
813                         logger.debug("putDRSub: trying to parse response as error: {}", ex.toString());
814                         // If this parse fails, let the exception be thrown
815                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
816                         response.setResponse(errResp);
817                 }
818                 return response;
819         }
820
821         /**
822          * Deletes the data router subscriber with the specified ID.
823          * 
824          * @param subId
825          *            ID of the subscriber to delete
826          * @return Status and response: expect 204 and a null on success; a code and
827          *         an ErrorResponse on failure.
828          * @throws Exception
829          *             if host cannot be reached, response cannot be parsed, etc.
830          */
831         public HttpStatusAndResponse<Object> deleteDRSub(final String subId) throws Exception {
832                 HttpStatusAndResponse<String> hsr = deleteRestContent(buildDmaapUri(DR_SUBS, subId));
833                 logger.debug("deleteDRSub: resp is {}", hsr);
834                 if (hsr == null)
835                         throw new Exception("deleteDRSub: unexpected null response");
836                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
837                 if (hsr.getResponse() == null) {
838                         logger.debug("deleteDRSub: null response body");
839                         return response;
840                 }
841                 try {
842                         DR_Sub resp = mapper.readValue(hsr.getResponseString(), DR_Sub.class);
843                         response.setResponse(resp);
844                 } catch (Exception ex) {
845                         logger.debug("deleteDRSub: trying to parse response as error: {}", ex.toString());
846                         // If this parse fails, let the exception be thrown
847                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
848                         response.setResponse(errResp);
849                 }
850                 return response;
851         }
852
853         /////////////////////////////////////////////////////////////////////
854
855         /**
856          * Gets a list of data router feeds.
857          * 
858          * @return List of DmaapObject: list contains DcaeLocation object(s) on
859          *         success; a single ErrorResponse object if the remote site rejects
860          *         the request.
861          * @throws Exception
862          *             if host cannot be reached, response cannot be parsed, etc.
863          */
864         public List<DmaapObject> getFeeds() throws Exception {
865                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(FEEDS));
866                 logger.debug("getFeeds: resp is {}", hsr);
867                 if (hsr == null)
868                         throw new Exception("getFeeds: unexpected null response");
869                 List<DmaapObject> responseList = null;
870                 try {
871                         TypeReference<List<Feed>> typeRef = new TypeReference<List<Feed>>() {
872                         };
873                         responseList = mapper.readValue(hsr.getResponseString(), typeRef);
874                 } catch (Exception ex) {
875                         logger.debug("getFeeds: trying to parse response as error: {}", ex.toString());
876                         // If this parse fails, let the exception be thrown
877                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
878                         responseList = new ArrayList<DmaapObject>();
879                         responseList.add(errResp);
880                 }
881                 return responseList;
882         }
883
884         /**
885          * Gets a data router feed with the specified ID.
886          * 
887          * @param feedId
888          *            ID of the feed to get
889          * @return DmaapObject: a Feed object on success; an ErrorResponse object if
890          *         the remote site rejects the request.
891          * @throws Exception
892          *             if host cannot be reached, response cannot be parsed, etc.
893          */
894         public DmaapObject getFeed(final String feedId) throws Exception {
895                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(FEEDS, feedId));
896                 logger.debug("getFeed: resp is {}", hsr);
897                 if (hsr == null)
898                         throw new Exception("getFeed: unexpected null response");
899                 DmaapObject response = null;
900                 try {
901                         response = mapper.readValue(hsr.getResponseString(), Feed.class);
902                 } catch (Exception ex) {
903                         logger.debug("getFeed: trying to parse response as error: {}", ex.toString());
904                         // If this parse fails, let the exception be thrown
905                         response = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
906                 }
907                 return response;
908         }
909
910         /**
911          * Creates a feed and adds any specified pubs and subs.
912          * 
913          * @param feed
914          *            Data router feed properties
915          * @return Status and response: expect 200 and a Feed on success; a code and
916          *         an ErrorResponse on failure.
917          * @throws Exception
918          *             if host cannot be reached, response cannot be parsed, etc.
919          */
920         public HttpStatusAndResponse<Object> postFeed(Feed feed) throws Exception {
921                 String jsonBody = mapper.writeValueAsString(feed);
922                 HttpStatusAndResponse<String> hsr = postRestContent(buildDmaapUri(FEEDS), jsonBody);
923                 logger.debug("postFeed: resp is {}", hsr);
924                 if (hsr == null)
925                         throw new Exception("postFeed: unexpected null response");
926                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
927                 if (hsr.getResponse() == null) {
928                         logger.debug("postFeed: null response body");
929                         return response;
930                 }
931                 try {
932                         Feed resp = mapper.readValue(hsr.getResponseString(), Feed.class);
933                         response.setResponse(resp);
934                 } catch (Exception ex) {
935                         logger.debug("postFeed: trying to parse response as error: {}", ex.toString());
936                         // If this parse fails, let the exception be thrown
937                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
938                         response.setResponse(errResp);
939                 }
940                 return response;
941         }
942
943         /**
944          * Updates a data router feed.
945          * 
946          * @param feed
947          *            Feed to be updated
948          * @return Status and response: expect 200 and a Feed on success; a code and
949          *         an ErrorResponse on failure.
950          * @throws Exception
951          *             if host cannot be reached, response cannot be parsed, etc.
952          */
953         public HttpStatusAndResponse<Object> putFeed(Feed feed) throws Exception {
954                 String jsonBody = mapper.writeValueAsString(feed);
955                 HttpStatusAndResponse<String> hsr = putRestContent(buildDmaapUri(FEEDS, feed.getFeedId()), jsonBody);
956                 logger.debug("putFeed: resp is {}", hsr);
957                 if (hsr == null)
958                         throw new Exception("putFeed: unexpected null response");
959                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
960                 if (hsr.getResponse() == null) {
961                         logger.debug("putFeed: null response body");
962                         return response;
963                 }
964                 try {
965                         Feed resp = mapper.readValue(hsr.getResponseString(), Feed.class);
966                         response.setResponse(resp);
967                 } catch (Exception ex) {
968                         logger.debug("putFeed: trying to parse response as error: {}", ex.toString());
969                         // If this parse fails, let the exception be thrown
970                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
971                         response.setResponse(errResp);
972                 }
973                 return response;
974         }
975
976         /**
977          * Deletes the data router feed with the specified ID.
978          * 
979          * @param feedId
980          *            ID of the feed to delete
981          * @return Status and response: expect 204 and a null on success; a code and
982          *         an ErrorResponse on failure.
983          * @throws Exception
984          *             if host cannot be reached, response cannot be parsed, etc.
985          */
986         public HttpStatusAndResponse<Object> deleteFeed(final String feedId) throws Exception {
987                 HttpStatusAndResponse<String> hsr = deleteRestContent(buildDmaapUri(FEEDS, feedId));
988                 logger.debug("deleteFeed: resp is {}", hsr);
989                 if (hsr == null)
990                         throw new Exception("deleteFeed: unexpected null response");
991                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
992                 if (hsr.getResponse() == null) {
993                         logger.debug("deleteFeed: null response body");
994                         return response;
995                 }
996                 try {
997                         Feed resp = mapper.readValue(hsr.getResponseString(), Feed.class);
998                         response.setResponse(resp);
999                 } catch (Exception ex) {
1000                         logger.debug("deleteFeed: trying to parse response as error: {}", ex.toString());
1001                         // If this parse fails, let the exception be thrown
1002                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1003                         response.setResponse(errResp);
1004                 }
1005                 return response;
1006         }
1007
1008         /////////////////////////////////////////////////////////////////////
1009
1010         /**
1011          * Gets a list of message router topics.
1012          * 
1013          * @return List of DmaapObject: list contains Topic object(s) on success; a
1014          *         single ErrorResponse object if the remote site rejects the
1015          *         request.
1016          * @throws Exception
1017          *             if host cannot be reached, response cannot be parsed, etc.
1018          */
1019         public List<DmaapObject> getTopics() throws Exception {
1020                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(TOPICS));
1021                 logger.debug("getTopics: resp is {}", hsr);
1022                 if (hsr == null)
1023                         throw new Exception("getTopics: unexpected null response");
1024                 List<DmaapObject> responseList = null;
1025                 try {
1026                         TypeReference<List<Topic>> typeRef = new TypeReference<List<Topic>>() {
1027                         };
1028                         responseList = mapper.readValue(hsr.getResponseString(), typeRef);
1029                 } catch (Exception ex) {
1030                         logger.debug("getTopics: trying to parse response as error: {}", ex.toString());
1031                         // If this parse fails, let the exception be thrown
1032                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1033                         responseList = new ArrayList<DmaapObject>();
1034                         responseList.add(errResp);
1035                 }
1036                 return responseList;
1037         }
1038
1039         /**
1040          * Gets the message router topic with the specified FQTN.
1041          * 
1042          * @param fqtn
1043          *            Fully qualified topic name
1044          * @return DmaapObject: a Topic object on success; an ErrorResponse object
1045          *         if the remote site rejects the request.
1046          * @throws Exception
1047          *             if host cannot be reached, response cannot be parsed, etc.
1048          */
1049         public DmaapObject getTopic(final String fqtn) throws Exception {
1050                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(TOPICS, fqtn));
1051                 logger.debug("getTopic: resp is {}", hsr);
1052                 if (hsr == null)
1053                         throw new Exception("getTopic: unexpected null response");
1054                 DmaapObject response = null;
1055                 try {
1056                         response = mapper.readValue(hsr.getResponseString(), Topic.class);
1057                 } catch (Exception ex) {
1058                         logger.debug("getTopic: trying to parse response as error: {}", ex.toString());
1059                         // If this parse fails, let the exception be thrown
1060                         response = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1061                 }
1062                 return response;
1063         }
1064
1065         /**
1066          * Creates a topic and grants appropriate permissions to specified pubs and
1067          * subs.
1068          * 
1069          * @param topic
1070          *            Message router topic properties
1071          * @return Status and response: expect 200 and a Topic on success; a code
1072          *         and an ErrorResponse on failure.
1073          * @throws Exception
1074          *             if host cannot be reached, response cannot be parsed, etc.
1075          */
1076         public HttpStatusAndResponse<Object> postTopic(Topic topic) throws Exception {
1077                 String jsonBody = mapper.writeValueAsString(topic);
1078                 HttpStatusAndResponse<String> hsr = postRestContent(buildDmaapUri(TOPICS), jsonBody);
1079                 logger.debug("postTopic: resp is {}", hsr);
1080                 if (hsr == null)
1081                         throw new Exception("postTopic: unexpected null response");
1082                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
1083                 if (hsr.getResponse() == null) {
1084                         logger.debug("postTopic: null response body");
1085                         return response;
1086                 }
1087                 try {
1088                         Topic resp = mapper.readValue(hsr.getResponseString(), Topic.class);
1089                         response.setResponse(resp);
1090                 } catch (Exception ex) {
1091                         logger.debug("postTopic: trying to parse response as error: {}", ex.toString());
1092                         // If this parse fails, let the exception be thrown
1093                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1094                         response.setResponse(errResp);
1095                 }
1096                 return response;
1097         }
1098
1099         /**
1100          * Deletes the message router topic with the specified FQTN.
1101          * 
1102          * @param fqtn
1103          *            Fully qualified topic name to delete
1104          * @return Status and response: expect 204 and a null on success; a code and
1105          *         an ErrorResponse on failure.
1106          * @throws Exception
1107          *             if host cannot be reached, response cannot be parsed, etc.
1108          */
1109         public HttpStatusAndResponse<Object> deleteTopic(final String fqtn) throws Exception {
1110                 HttpStatusAndResponse<String> hsr = deleteRestContent(buildDmaapUri(TOPICS, fqtn));
1111                 logger.debug("deleteTopic: resp is {}", hsr);
1112                 if (hsr == null)
1113                         throw new Exception("deleteTopic: unexpected null response");
1114                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
1115                 if (hsr.getResponse() == null) {
1116                         logger.debug("deleteTopic: null response body");
1117                         return response;
1118                 }
1119                 try {
1120                         Topic resp = mapper.readValue(hsr.getResponseString(), Topic.class);
1121                         response.setResponse(resp);
1122                 } catch (Exception ex) {
1123                         logger.debug("deleteTopic: trying to parse response as error: {}", ex.toString());
1124                         // If this parse fails, let the exception be thrown
1125                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1126                         response.setResponse(errResp);
1127                 }
1128                 return response;
1129         }
1130
1131         /////////////////////////////////////////////////////////////////////
1132
1133         /**
1134          * Gets a list of message router clients.
1135          * 
1136          * @return List of DmaapObject: list contains MR_Client object(s) on
1137          *         success; a single ErrorResponse object if the remote site rejects
1138          *         the request.
1139          * @throws Exception
1140          *             if host cannot be reached, response cannot be parsed, etc.
1141          */
1142         public List<DmaapObject> getMRClients() throws Exception {
1143                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(MR_CLIENTS));
1144                 logger.debug("getMRClients: resp is {}", hsr);
1145                 if (hsr == null)
1146                         throw new Exception("getMRClients: unexpected null response");
1147                 List<DmaapObject> responseList = null;
1148                 try {
1149                         TypeReference<List<MR_Client>> typeRef = new TypeReference<List<MR_Client>>() {
1150                         };
1151                         responseList = mapper.readValue(hsr.getResponseString(), typeRef);
1152                 } catch (Exception ex) {
1153                         logger.debug("getMRClients: trying to parse response as error: {}", ex.toString());
1154                         // If this parse fails, let the exception be thrown
1155                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1156                         responseList = new ArrayList<DmaapObject>();
1157                         responseList.add(errResp);
1158                 }
1159                 return responseList;
1160         }
1161
1162         /**
1163          * Gets the message router client with the specified ID.
1164          * 
1165          * @param mrClientId
1166          *            ID of the client to get
1167          * @return DmaapObject: a MR_Client object on success; an ErrorResponse
1168          *         object if the remote site rejects the request.
1169          * @throws Exception
1170          *             if host cannot be reached, response cannot be parsed, etc.
1171          */
1172         public DmaapObject getMRClient(final String mrClientId) throws Exception {
1173                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(MR_CLIENTS, mrClientId));
1174                 logger.debug("getMRClient: resp is {}", hsr);
1175                 if (hsr == null)
1176                         throw new Exception("getMRClient: unexpected null response");
1177                 DmaapObject response = null;
1178                 try {
1179                         response = mapper.readValue(hsr.getResponseString(), MR_Client.class);
1180                 } catch (Exception ex) {
1181                         logger.debug("getMRClient: trying to parse response as error: {}", ex.toString());
1182                         // If this parse fails, let the exception be thrown
1183                         response = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1184                 }
1185                 return response;
1186         }
1187
1188         /**
1189          * Creates a message router client.
1190          * 
1191          * @param mrClient
1192          *            Message router client properties
1193          * @return Status and response: expect 200 and a MR_Client on success; a
1194          *         code and an ErrorResponse on failure.
1195          * @throws Exception
1196          *             if host cannot be reached, response cannot be parsed, etc.
1197          */
1198         public HttpStatusAndResponse<Object> postMRClient(MR_Client mrClient) throws Exception {
1199                 String jsonBody = mapper.writeValueAsString(mrClient);
1200                 HttpStatusAndResponse<String> hsr = postRestContent(buildDmaapUri(MR_CLIENTS), jsonBody);
1201                 logger.debug("postMRClient: resp is {}", hsr);
1202                 if (hsr == null)
1203                         throw new Exception("postMRClient: unexpected null response");
1204                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
1205                 if (hsr.getResponse() == null) {
1206                         logger.debug("postMRClient: null response body");
1207                         return response;
1208                 }
1209                 try {
1210                         MR_Client resp = mapper.readValue(hsr.getResponseString(), MR_Client.class);
1211                         response.setResponse(resp);
1212                 } catch (Exception ex) {
1213                         logger.debug("postMRClient: trying to parse response as error: {}", ex.toString());
1214                         // If this parse fails, let the exception be thrown
1215                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1216                         response.setResponse(errResp);
1217                 }
1218                 return response;
1219         }
1220
1221         /**
1222          * Updates a message router client.
1223          * 
1224          * @param mrClient
1225          *            client to be updated
1226          * @return Status and response; expect 200 and a MR_Client on success, a
1227          *         string error on failure.
1228          * @throws Exception
1229          *             if host cannot be reached, response cannot be parsed, etc.
1230          */
1231         public HttpStatusAndResponse<Object> putMRClient(MR_Client mrClient) throws Exception {
1232                 String jsonBody = mapper.writeValueAsString(mrClient);
1233                 HttpStatusAndResponse<String> hsr = putRestContent(buildDmaapUri(MR_CLIENTS, mrClient.getMrClientId()),
1234                                 jsonBody);
1235                 logger.debug("putMRClient: resp is {}", hsr);
1236                 if (hsr == null)
1237                         throw new Exception("putMRClient: unexpected null response");
1238                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
1239                 if (hsr.getResponse() == null) {
1240                         logger.debug("putMRClient: null response body");
1241                         return response;
1242                 }
1243                 try {
1244                         MR_Client resp = mapper.readValue(hsr.getResponseString(), MR_Client.class);
1245                         response.setResponse(resp);
1246                 } catch (Exception ex) {
1247                         logger.debug("putMRClient: trying to parse response as error: {}", ex.toString());
1248                         // If this parse fails, let the exception be thrown
1249                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1250                         response.setResponse(errResp);
1251                 }
1252                 return response;
1253         }
1254
1255         /**
1256          * Deletes the message router client with the specified ID.
1257          * 
1258          * @param mrClientId
1259          *            ID of the client to delete
1260          * @return Status and response; expect 204 and a null on success, a string
1261          *         error on failure.
1262          * @throws Exception
1263          *             if host cannot be reached, response cannot be parsed, etc.
1264          */
1265         public HttpStatusAndResponse<Object> deleteMRClient(final String mrClientId) throws Exception {
1266                 HttpStatusAndResponse<String> hsr = deleteRestContent(buildDmaapUri(MR_CLIENTS, mrClientId));
1267                 logger.debug("deleteMRClient: resp is {}", hsr);
1268                 if (hsr == null)
1269                         throw new Exception("deleteMRClient: unexpected null response");
1270                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
1271                 if (hsr.getResponse() == null) {
1272                         logger.debug("deleteMRClient: null response body");
1273                         return response;
1274                 }
1275                 try {
1276                         MR_Client resp = mapper.readValue(hsr.getResponseString(), MR_Client.class);
1277                         response.setResponse(resp);
1278                 } catch (Exception ex) {
1279                         logger.debug("deleteMRClient: trying to parse response as error: {}", ex.toString());
1280                         // If this parse fails, let the exception be thrown
1281                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1282                         response.setResponse(errResp);
1283                 }
1284                 return response;
1285         }
1286
1287         /////////////////////////////////////////////////////////////////////
1288
1289         /**
1290          * Gets a list of message router clusters.
1291          * 
1292          * @return List of DmaapObject: list contains MR_Cluster object(s) on
1293          *         success; a single ErrorResponse object if the remote site rejects
1294          *         the request.
1295          * @throws Exception
1296          *             if host cannot be reached, response cannot be parsed, etc.
1297          */
1298         public List<DmaapObject> getMRClusters() throws Exception {
1299                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(MR_CLUSTERS));
1300                 logger.debug("getMRClusters: resp is {}", hsr);
1301                 if (hsr == null)
1302                         throw new Exception("getMRClusters: unexpected null response");
1303                 List<DmaapObject> responseList = null;
1304                 try {
1305                         TypeReference<List<MR_Cluster>> typeRef = new TypeReference<List<MR_Cluster>>() {
1306                         };
1307                         responseList = mapper.readValue(hsr.getResponseString(), typeRef);
1308                 } catch (Exception ex) {
1309                         logger.debug("getDcaeLocations: trying to parse response as error: {}", ex.toString());
1310                         // If this parse fails, let the exception be thrown
1311                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1312                         responseList = new ArrayList<DmaapObject>();
1313                         responseList.add(errResp);
1314                 }
1315                 return responseList;
1316         }
1317
1318         /**
1319          * Gets the message router cluster with the specified location name.
1320          * 
1321          * @param dcaeLocName
1322          *            name of the cluster to get
1323          * @return DmaapObject: a MR_Cluster object on success; an ErrorResponse
1324          *         object if the remote site rejects the request.
1325          * @throws Exception
1326          *             if host cannot be reached, response cannot be parsed, etc.
1327          */
1328         public DmaapObject getMRCluster(final String dcaeLocName) throws Exception {
1329                 HttpStatusAndResponse<String> hsr = getRestContent(buildDmaapUri(MR_CLUSTERS, dcaeLocName));
1330                 logger.debug("getMRCluster: resp is {}", hsr);
1331                 if (hsr == null)
1332                         throw new Exception("getMRCluster: unexpected null response");
1333                 DmaapObject response = null;
1334                 try {
1335                         response = mapper.readValue(hsr.getResponseString(), MR_Cluster.class);
1336                 } catch (Exception ex) {
1337                         logger.debug("getMRCluster: trying to parse response as error: {}", ex.toString());
1338                         // If this parse fails, let the exception be thrown
1339                         response = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1340                 }
1341                 return response;
1342         }
1343
1344         /**
1345          * Creates a message router cluster.
1346          * 
1347          * @param mrCluster
1348          *            Message router cluster properties
1349          * @return Status and response: expect 200 and a MR_Cluster on success; a
1350          *         code and an ErrorResponse on failure.
1351          * @throws Exception
1352          *             if host cannot be reached, response cannot be parsed, etc.
1353          */
1354         public HttpStatusAndResponse<Object> postMRCluster(MR_Cluster mrCluster) throws Exception {
1355                 String jsonBody = mapper.writeValueAsString(mrCluster);
1356                 HttpStatusAndResponse<String> hsr = postRestContent(buildDmaapUri(MR_CLUSTERS), jsonBody);
1357                 logger.debug("postMRCluster: resp is {}", hsr);
1358                 if (hsr == null)
1359                         throw new Exception("postMRCluster: unexpected null response");
1360                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
1361                 if (hsr.getResponse() == null) {
1362                         logger.debug("postMRCluster: null response body");
1363                         return response;
1364                 }
1365                 try {
1366                         MR_Cluster resp = mapper.readValue(hsr.getResponseString(), MR_Cluster.class);
1367                         response.setResponse(resp);
1368                 } catch (Exception ex) {
1369                         logger.debug("postMRCluster: trying to parse response as error: {}", ex.toString());
1370                         // If this parse fails, let the exception be thrown
1371                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1372                         response.setResponse(errResp);
1373                 }
1374                 return response;
1375         }
1376
1377         /**
1378          * Updates a message router cluster.
1379          * 
1380          * @param mrCluster
1381          *            cluster to be updated
1382          * @return Status and response; expect 200 and a MR_Cluster on success, a
1383          *         string error on failure.
1384          * @throws Exception
1385          *             if host cannot be reached, response cannot be parsed, etc.
1386          */
1387         public HttpStatusAndResponse<Object> putMRCluster(MR_Cluster mrCluster) throws Exception {
1388                 String jsonBody = mapper.writeValueAsString(mrCluster);
1389                 HttpStatusAndResponse<String> hsr = putRestContent(buildDmaapUri(MR_CLUSTERS, mrCluster.getDcaeLocationName()),
1390                                 jsonBody);
1391                 logger.debug("putMRCluster: resp is {}", hsr);
1392                 if (hsr == null)
1393                         throw new Exception("putMRCluster: unexpected null response");
1394                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
1395                 if (hsr.getResponse() == null) {
1396                         logger.debug("putMRCluster: null response body");
1397                         return response;
1398                 }
1399                 try {
1400                         MR_Cluster resp = mapper.readValue(hsr.getResponseString(), MR_Cluster.class);
1401                         response.setResponse(resp);
1402                 } catch (Exception ex) {
1403                         logger.debug("putMRCluster: trying to parse response as error: {}", ex.toString());
1404                         // If this parse fails, let the exception be thrown
1405                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1406                         response.setResponse(errResp);
1407                 }
1408                 return response;
1409         }
1410
1411         /**
1412          * Deletes the message router cluster with the specified location name.
1413          * 
1414          * @param dcaeLocName
1415          *            Location name of the cluster to delete
1416          * @return Status and response: expect 204 and a null on success; a code and
1417          *         an ErrorResponse on failure.
1418          * @throws Exception
1419          *             if host cannot be reached, response cannot be parsed, etc.
1420          */
1421         public HttpStatusAndResponse<Object> deleteMRCluster(final String dcaeLocName) throws Exception {
1422                 HttpStatusAndResponse<String> hsr = deleteRestContent(buildDmaapUri(MR_CLUSTERS, dcaeLocName));
1423                 if (hsr == null)
1424                         throw new Exception("deleteMRCluster: unexpected null response");
1425                 logger.debug("deleteMRCluster: resp is {}", hsr);
1426                 HttpStatusAndResponse<Object> response = new HttpStatusAndResponse<Object>(hsr.getStatusCode(), null);
1427                 if (hsr.getResponse() == null) {
1428                         logger.debug("deleteMRCluster: null response body");
1429                         return response;
1430                 }
1431                 try {
1432                         MR_Cluster resp = mapper.readValue(hsr.getResponseString(), MR_Cluster.class);
1433                         response.setResponse(resp);
1434                 } catch (Exception ex) {
1435                         logger.debug("deleteMRCluster: trying to parse response as error: {}", ex.toString());
1436                         // If this parse fails, let the exception be thrown
1437                         ErrorResponse errResp = mapper.readValue(hsr.getResponseString(), ErrorResponse.class);
1438                         response.setResponse(errResp);
1439                 }
1440                 return response;
1441         }
1442
1443         /////////////////////////////////////////////////////////////////////
1444
1445         /**
1446          * Builds the URI for the DMaaP REST endpoint using configuration and the
1447          * specified task and path parameter(s). Deals with extra or missing slashes
1448          * to allow for some flexibility in the config file.
1449          * 
1450          * @param requestPath
1451          *            Last part of endpoint path
1452          * @param pathParam
1453          *            Additional path parameters in order; ignored if null or empty
1454          * @return REST endpoint URI
1455          * @throws Exception
1456          *             if the RESAT URL property is not found
1457          */
1458         private URI buildDmaapUri(String task, String... pathParam) throws Exception {
1459                 if (dmaapRestUrl == null || dmaapRestUrl.length() == 0)
1460                         throw new Exception("buildUrlPath: unconfigured, must set dmaapEndpointUrl");
1461                 StringBuilder sb = new StringBuilder();
1462                 // Clean the base of any trailing slashes
1463                 sb.append(trimSlashes(dmaapRestUrl));
1464                 sb.append('/');
1465                 // task is controlled in this file, don't clean it.
1466                 sb.append(task);
1467                 if (pathParam != null) {
1468                         for (String pp : pathParam) {
1469                                 sb.append('/');
1470                                 // path comes from the user, definitely clean it.
1471                                 sb.append(trimSlashes(pp));
1472                         }
1473                 }
1474                 String urlPath = sb.toString();
1475                 URIBuilder uriBuilder = new URIBuilder(urlPath);
1476                 return uriBuilder.build();
1477         }
1478
1479         /**
1480          * Strips the specified string of leading and trailing forward-slash
1481          * characters.
1482          * 
1483          * @param s
1484          *            String to trim
1485          * @return String without any leading or trailing '/' characters.
1486          */
1487         private String trimSlashes(String s) {
1488                 while (s.length() > 0 && s.charAt(0) == '/')
1489                         s = s.substring(1, s.length());
1490                 while (s.length() > 0 && s.charAt(s.length() - 1) == '/')
1491                         s = s.substring(0, s.length() - 1);
1492                 return s;
1493         }
1494
1495 }