diff options
Diffstat (limited to 'dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller')
7 files changed, 2216 insertions, 0 deletions
diff --git a/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DataBusHomeController.java b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DataBusHomeController.java new file mode 100644 index 0000000..830c1fa --- /dev/null +++ b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DataBusHomeController.java @@ -0,0 +1,57 @@ +/*- + * ================================================================================ + * DCAE DMaaP Bus Controller Web Application + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.dmaapbc.dbcapp.controller; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +import javax.servlet.ServletException; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.ModelAndView; + +/** + * This controller maps requests for the DMaaP-BC-App's landing page, which is + * an Angular single-page application. + */ +@Controller +@RequestMapping("/") +public class DataBusHomeController extends DbcappRestrictedBaseController { + + public static final String APP_NAME = "dmaap-bc-app"; + public static final DateFormat logDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + + /** + * At one time I published the landing page as simply "/dbc", but it can + * also be accessed with a default suffix; e.g., "/dbc.htm". + * + * @return View name key, which is resolved to a file using an Apache tiles + * "definitions.xml" file. + * @throws ServletException + */ + @RequestMapping(value = { "/dbc" }, method = RequestMethod.GET) + public ModelAndView dbcDefaultController() throws ServletException { + // a model is only useful for JSP; this app is angular. + return new ModelAndView("dbc_home_tdkey"); + } + +} diff --git a/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DataRouterController.java b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DataRouterController.java new file mode 100644 index 0000000..27f5dd6 --- /dev/null +++ b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DataRouterController.java @@ -0,0 +1,315 @@ +/*- + * ================================================================================ + * DCAE DMaaP Bus Controller Web Application + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.dmaapbc.dbcapp.controller; + +import java.util.Date; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.slf4j.MDC; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * Data Router controller: serves Ajax requests made by Angular scripts on pages + * that show feeds, publishers and subscribers. + */ +@Controller +@RequestMapping("/") +public class DataRouterController extends DbcappRestrictedBaseController { + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(DataRouterController.class); + + private static final String FEED_PATH = "/dr_feed"; + private static final String PUB_PATH = "/dr_pub"; + private static final String SUB_PATH = "/dr_sub"; + + public DataRouterController() { + } + + /** + * Answers a request for one page of data router feeds. + * + * @param request + * @return Result of + * {@link #getItemListForPageWrapper(HttpServletRequest, DmaapDataItem)} + * @throws ServletException + */ + @RequestMapping(value = { FEED_PATH }, method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public String getDRFeedsByPage(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = getItemListForPageWrapper(request, DmaapDataItem.DR_FEED); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Answers a request for one page of data router publishers. + * + * @param request + * + * @return Result of + * {@link #getItemListForPageWrapper(HttpServletRequest, DmaapDataItem)} + * @throws ServletException + */ + @RequestMapping(value = { PUB_PATH }, method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public String getDRPubsByPage(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = getItemListForPageWrapper(request, DmaapDataItem.DR_PUB); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Answers a request for one page of data router subscribers. + * + * @param request + * + * @return Result of + * {@link #getItemListForPageWrapper(HttpServletRequest, DmaapDataItem)} + * @throws ServletException + */ + @RequestMapping(value = { SUB_PATH }, method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public String getDRSubsByPage(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = getItemListForPageWrapper(request, DmaapDataItem.DR_SUB); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Adds a feed with the specified information. Expects a JSON block in the + * request body - a Feed object. + * + * @param request + * @return a JSON object; on success it has a "status" and possibly a "data" + * item; on failure, also has an "error" item. + * @throws ServletException + */ + @RequestMapping(value = { FEED_PATH }, method = RequestMethod.POST, produces = "application/json") + @ResponseBody + public String addDRFeed(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = addItem(request, DmaapDataItem.DR_FEED, null); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Adds a publisher with the specified information. Expects a JSON block in + * the request body - a DR_Pub object. + * + * @return a JSON object; on success it has a "status" and possibly a "data" + * item; on failure, also has an "error" item. + * + * @param request + * @throws ServletException + */ + @RequestMapping(value = { PUB_PATH }, method = RequestMethod.POST, produces = "application/json") + @ResponseBody + public String addDRPub(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = addItem(request, DmaapDataItem.DR_PUB, HttpServletResponse.SC_CREATED); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Adds a subscriber with the specified information. Expects a JSON block in + * the request body - a DR_Sub object. + * + * @return a JSON object; on success it has a "status" and possibly a "data" + * item; on failure, also has an "error" item. + * + * @param request + * @throws ServletException + */ + @RequestMapping(value = { SUB_PATH }, method = RequestMethod.POST, produces = "application/json") + @ResponseBody + public String addDRSub(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = addItem(request, DmaapDataItem.DR_SUB, HttpServletResponse.SC_CREATED); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Updates a feed with the specified information. Expects a JSON block in + * the request body - a Feed object. + * + * @param id + * Path parameter with object ID + * @param request + * @return a JSON object; on success it has a "status" and possibly a "data" + * item; on failure, also has an "error" item. + * @throws ServletException + */ + @RequestMapping(value = { FEED_PATH + "/{id}" }, method = RequestMethod.PUT, produces = "application/json") + @ResponseBody + public String updateFeed(@PathVariable("id") long id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = updateItem(request, DmaapDataItem.DR_FEED, Long.toString(id), null); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Updates a publisher with the specified information. Expects a JSON block + * in the request body - a DR_Pub object. + * + * The pubId may have a dot in it. Spring, in its infinite wisdom, truncates + * extensions on dotted path parameters; e.g., "foo.json" becomes "foo". + * Avoid truncation here with the extra ":.+" incantation at the end. + * + * @param id + * Path parameter with object ID + * @param request + * @return a JSON object; on success it has a "status" and possibly a "data" + * item; on failure, also has an "error" item. + * @throws ServletException + */ + @RequestMapping(value = { PUB_PATH + "/{id:.+}" }, method = RequestMethod.PUT, produces = "application/json") + @ResponseBody + public String updateDRPub(@PathVariable("id") String id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = updateItem(request, DmaapDataItem.DR_PUB, id, null); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Updates a subscriber with the specified information. Expects a JSON block + * in the request body - a DR_Sub object. + * + * @param id + * Path parameter with object ID + * @param request + * @return a JSON object; on success it has a "status" and possibly a "data" + * item; on failure, also has an "error" item. + * @throws ServletException + */ + @RequestMapping(value = { SUB_PATH + "/{id}" }, method = RequestMethod.PUT, produces = "application/json") + @ResponseBody + public String updateDRSub(@PathVariable("id") long id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = updateItem(request, DmaapDataItem.DR_SUB, Long.toString(id), null); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Deletes a feed with the ID specified as a path parameter. On successful + * delete the endpoint returns 204 (confusingly). + * + * Writes a JSON object as an HTTP response; on success it only has "status" + * item; on failure, also has an "error" item. + * + * @param id + * Path parameter with object ID + * @param request + * @return JSON object + * @throws ServletException + */ + @RequestMapping(value = { FEED_PATH + "/{id}" }, method = RequestMethod.DELETE, produces = "application/json") + @ResponseBody + public String deleteDRFeed(@PathVariable("id") long id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = deleteItem(request, DmaapDataItem.DR_FEED, Long.toString(id), 204); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Deletes the publisher with the ID specified as a path parameter. + * + * The pubId may have a dot in it. Spring, in its infinite wisdom, truncates + * extensions on dotted path parameters; e.g., "foo.json" becomes "foo". + * Avoid truncation here with the extra ":.+" incantation at the end. + * + * @param id + * Path parameter with object ID + * @param request + * @return a JSON object; on success it only has "status" item; on failure, + * also has an "error" item. + * @throws ServletException + */ + @RequestMapping(value = { PUB_PATH + "/{id:.+}" }, method = RequestMethod.DELETE, produces = "application/json") + @ResponseBody + public String deleteDRPub(@PathVariable("id") String id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = deleteItem(request, DmaapDataItem.DR_PUB, id, null); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Deletes the subscriber with the ID specified as a path parameter. + * + * @param id + * Path parameter with object ID + * @param request + * @return Writes a JSON object; on success it only has "status" item; on + * failure, also has an "error" item. + * @throws ServletException + */ + @RequestMapping(value = { SUB_PATH + "/{id}" }, method = RequestMethod.DELETE, produces = "application/json") + @ResponseBody + public String deleteDRSub(@PathVariable("id") long id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = deleteItem(request, DmaapDataItem.DR_SUB, Long.toString(id), 204); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + +} diff --git a/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DbcappRestrictedBaseController.java b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DbcappRestrictedBaseController.java new file mode 100644 index 0000000..a527127 --- /dev/null +++ b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DbcappRestrictedBaseController.java @@ -0,0 +1,974 @@ +/*- + * ================================================================================ + * DCAE DMaaP Bus Controller Web Application + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.dmaapbc.dbcapp.controller; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.dcae.dmaap.bcapi.client.DmaapBcRestClient; +import org.openecomp.dcae.dmaap.bcapi.client.HttpStatusAndResponse; +import org.openecomp.dcae.dmaapbc.model.DR_Pub; +import org.openecomp.dcae.dmaapbc.model.DR_Sub; +import org.openecomp.dcae.dmaapbc.model.DcaeLocation; +import org.openecomp.dcae.dmaapbc.model.Dmaap; +import org.openecomp.dcae.dmaapbc.model.DmaapObject; +import org.openecomp.dcae.dmaapbc.model.ErrorResponse; +import org.openecomp.dcae.dmaapbc.model.Feed; +import org.openecomp.dcae.dmaapbc.model.MR_Client; +import org.openecomp.dcae.dmaapbc.model.Topic; +import org.openecomp.dmaapbc.dbcapp.domain.DmaapAccess; +import org.openecomp.dmaapbc.dbcapp.rest.DbcUsvcRestClient; +import org.openecomp.dmaapbc.dbcapp.service.DmaapAccessService; +import org.openecomp.dmaapbc.dbcapp.util.DbcappProperties; +import org.openecomp.portalsdk.core.controller.RestrictedBaseController; +import org.openecomp.portalsdk.core.domain.User; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.CipherUtil; +import org.openecomp.portalsdk.core.web.support.UserUtils; +import org.springframework.beans.factory.annotation.Autowired; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * This base class provides utility methods to child controllers. All of the + * requests are forwarded on to a remote REST API, so there's a large degree of + * commonality among the implementations. Combining them kept the lines-of-code + * count down, at the expense of some complexity. + */ +public class DbcappRestrictedBaseController extends RestrictedBaseController { + + /** + * Query parameter for desired page number + */ + protected static final String PAGE_NUM_QUERY_PARAM = "pageNum"; + + /** + * Query parameter for desired items per page + */ + protected static final String VIEW_PER_PAGE_QUERY_PARAM = "viewPerPage"; + + /** + * Tag for status code in JSON responses - ALWAYS PRESENT. + */ + protected static final String STATUS_RESPONSE_KEY = "status"; + + /** + * Tag for data in JSON responses. + */ + protected static final String DATA_RESPONSE_KEY = "data"; + + /** + * Tag for error message in JSON responses; absent on success. + */ + protected static final String ERROR_RESPONSE_KEY = "error"; + + /** + * Tag for response integer, pages required to display complete result list + */ + protected static final String TOTAL_PAGES_RESPONSE_KEY = "totalPages"; + + /** + * Tag for DMaaP name obtained from REST client. + */ + protected static final String PROFILE_NAME_RESPONSE_KEY = "profileName"; + + /** + * Tag for DMaaP name obtained from REST client. + */ + protected static final String DMAAP_NAME_RESPONSE_KEY = "dmaapName"; + + /** + * Tag for DCAE location name list obtained from REST client. + */ + protected static final String DCAE_LOCATIONS_RESPONSE_KEY = "dcaeLocations"; + + /** + * Logger that conforms with ECOMP guidelines + */ + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(DbcappRestrictedBaseController.class); + + /** + * For general use in these methods and subclasses + */ + protected final ObjectMapper mapper = new ObjectMapper(); + + /** + * DAO accesses the profiles via a local database. REST accesses the + * profiles via a remote REST service. + */ + public enum AccessMethod { + DAO, REST + }; + + /** + * Enum for selecting an item type. + */ + public enum DmaapDataItem { + DR_FEED, DR_PUB, DR_SUB, MR_TOPIC, MR_CLIENT; + } + + /** + * Application properties - NOT available to constructor. + */ + @Autowired + private DbcappProperties appProperties; + + /** + * Database access - which might not be used. + */ + @Autowired + private DmaapAccessService dmaapAccessDaoServiceAuto; + + /** + * Read from application properties. + */ + private String mechIdName, mechIdPass; + + /** + * This is set by {@link #getDmaapAccessService()} to the DAO or REST + * implementation as configured in properties. + */ + private DmaapAccessService dmaapAccessService; + + /** + * Hello Spring, here's your no-arg constructor. + */ + public DbcappRestrictedBaseController() { + // Do not serialize null values + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + } + + /** + * Access method for subclasses. + * + * @return DbcappProperties object that was autowired by Spring. + */ + protected DbcappProperties getAppProperties() { + return appProperties; + } + + /** + * Lazy initialization. As a side effect, caches mech ID and password. + * + * @return Either DAO or REST client that implements the access service + * interface. + */ + protected DmaapAccessService getDmaapAccessService() { + if (dmaapAccessService != null) + return dmaapAccessService; + + // Get the application's mechid + mechIdName = appProperties.getProperty(DbcappProperties.DMAAP_MECHID_NAME); + // This is encrypted + String cipher = appProperties.getProperty(DbcappProperties.DMAAP_MECHID_PASSWORD); + if (mechIdName == null || cipher == null) + throw new RuntimeException("Failed to get MECH_ID name and/or password from properties"); + try { + mechIdPass = CipherUtil.decrypt(cipher); + } catch (Exception ex) { + throw new RuntimeException("Failed to decrypt password from config file", ex); + } + + String accessMethod = appProperties.getProperty(DbcappProperties.PROFILE_ACCESS_METHOD); + if (accessMethod == null) + throw new RuntimeException("Failed to get property " + DbcappProperties.PROFILE_ACCESS_METHOD); + AccessMethod profileAccessMethod = AccessMethod.valueOf(accessMethod.toUpperCase()); + if (AccessMethod.DAO == profileAccessMethod) { + // Spring auto-wired this field + dmaapAccessService = dmaapAccessDaoServiceAuto; + } else { + String url = appProperties.getProperty(DbcappProperties.PROFILE_USVC_URL); + String user = appProperties.getProperty(DbcappProperties.PROFILE_USVC_USER); + String pass = appProperties.getProperty(DbcappProperties.PROFILE_USVC_PASS); + if (url == null || user == null || pass == null) + throw new RuntimeException("getDmaapAccessService: missing property: one of url, user, pass"); + String clearText = null; + try { + clearText = CipherUtil.decrypt(pass); + } catch (Exception ex) { + throw new RuntimeException("getDmaapAccessService: failed to decrypt password from config"); + } + dmaapAccessService = new DbcUsvcRestClient(url, user, clearText); + } + return dmaapAccessService; + } + + /** + * Creates a REST client with appropriate credentials, the user/pass from + * the access profile if present, otherwise with the default mech ID and + * password. + * + * @param dmaapAccess + * @return REST client + */ + protected DmaapBcRestClient getDmaapBcRestClient(DmaapAccess dmaapAccess) { + DmaapBcRestClient restClient = null; + if (dmaapAccess.getMechId() == null || dmaapAccess.getMechId().length() == 0) + restClient = new DmaapBcRestClient(dmaapAccess.getDmaapUrl(), mechIdName, mechIdPass); + else + restClient = new DmaapBcRestClient(dmaapAccess.getDmaapUrl(), dmaapAccess.getMechId(), + dmaapAccess.getPassword()); + return restClient; + } + + /** + * Pulls out of the specified list the appropriate items for the page of + * results specified by the page number and view-per-page parameters. + * + * @param pageNum + * Page number requested by user + * @param viewPerPage + * Number of items per page + * @param itemList + * List of items available + * @return List of items to display + */ + @SuppressWarnings("rawtypes") + private static List shrinkListToPage(final int pageNum, final int viewPerPage, final List itemList) { + // user-friendly page numbers index from 1 + int firstIndexOnThisPage = viewPerPage * (pageNum - 1); + int firstIndexOnNextPage = viewPerPage * pageNum; + int fromIndex = firstIndexOnThisPage < itemList.size() ? firstIndexOnThisPage : itemList.size(); + int toIndex = firstIndexOnNextPage < itemList.size() ? firstIndexOnNextPage : itemList.size(); + // answers empty list if from==to + return itemList.subList(fromIndex, toIndex); + } + + /** + * Gets the body of a HTTP request assuming UTF-8 encoding. + * + * @param request + * @return String version of request body + * @throws IOException If the read fails + */ + protected static String getBody(HttpServletRequest request) throws IOException { + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader bufferedReader = null; + try { + InputStream inputStream = request.getInputStream(); + if (inputStream != null) { + bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + char[] charBuffer = new char[512]; + int bytesRead = -1; + while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { + stringBuilder.append(charBuffer, 0, bytesRead); + } + } else { + stringBuilder.append(""); + } + } finally { + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (IOException ex) { + throw ex; + } + } + + } + return stringBuilder.toString(); + } + + /** + * Builds a JSON success response from the specified inputs. + * + * @param statusCode + * e.g., 200 for OK + * @param dataPojo + * Plain old Java object to serialize as JSON; ignored if null. + * @throws JsonProcessingException + * If the POJO cannot be serialized + * @return JSON block with items "status" : 200 and "data" : (data..) + */ + protected String buildJsonSuccess(int statusCode, Object dataPojo) throws JsonProcessingException { + Map<String, Object> model = new HashMap<String, Object>(); + model.put(STATUS_RESPONSE_KEY, statusCode); + if (dataPojo != null) + model.put(DATA_RESPONSE_KEY, dataPojo); + String json = mapper.writeValueAsString(model); + return json; + } + + /** + * Builds a JSON error response from the specified inputs. + * + * @param statusCode + * e.g., 500 for internal server error + * @param errMsg + * Information about the operation that failed + * @param exception + * Converted to string; ignored if null. + * @return JSON block with tags "status" and "error". + */ + protected String buildJsonError(int statusCode, String errMsg, Exception exception) { + Map<String, Object> model = new HashMap<String, Object>(); + model.put(STATUS_RESPONSE_KEY, new Integer(500)); + if (exception == null) { + model.put(ERROR_RESPONSE_KEY, errMsg); + } else { + final int enough = 512; + String exString = exception.toString(); + String exceptionMsg = exString.length() > enough ? exString.substring(0, enough) : exString; + model.put(ERROR_RESPONSE_KEY, errMsg + ": " + exceptionMsg); + } + String json = null; + try { + json = mapper.writeValueAsString(model); + } catch (JsonProcessingException ex) { + // serializing the trivial map should never fail + String err = "buildJsonError: failed to serialize"; + logger.error(EELFLoggerDelegate.errorLogger, err, ex); + throw new RuntimeException(err, ex); + } + return json; + } + + /** + * Gets a list of DMaaP access profiles for this user from the database. The + * profiles have passwords in the clear - this method decrypts the database + * entries. + * + * Initializes the list for new users and/or configuration changes. Checks + * the database list against the configured list of URLs, and creates new + * rows for any configured URLs not present for the user. Most environments + * are expected to have exactly one valid URL, and the webapp uses a fixed + * MechID to authenticate itself to the DMaaP bus controller, so this + * approach means new users can start without any setup of URLs. + * + * @param userId + * @return List of DmaapAccess objects + * @throws Exception + */ + protected List<DmaapAccess> getOrInitDmaapAccessList(String userId) throws Exception { + String[] configUrls = getAppProperties().getCsvListProperty(DbcappProperties.DMAAP_REST_URL_LIST); + if (configUrls == null || configUrls.length == 0) + throw new Exception("getOrInitDmaapAccessList: Failed to get DMAAP REST URL list"); + // Update this list to track which URLs are in the database. + List<String> configUrlList = new ArrayList<String>(configUrls.length); + for (String c : configUrls) { + // Validate URL to detect config botches + URL url = new URL(c); + configUrlList.add(url.toExternalForm()); + } + + List<DmaapAccess> dbAccessList = getDmaapAccessService().getDmaapAccessList(userId); + + // Check the database entries against the configuration. Also + // build a list of non-DAO objects with clear-text passwords. + List<DmaapAccess> clearList = new ArrayList<DmaapAccess>(dbAccessList.size()); + for (DmaapAccess dmaapAccess : dbAccessList) { + // drop this URL from the list. + // If it's not known to config, complain because that's a bogus row. + if (!configUrlList.remove(dmaapAccess.getDmaapUrl())) + logger.warn(EELFLoggerDelegate.errorLogger, "getOrInitDmaapAccessList: detected extra URL {}", + dmaapAccess.getDmaapUrl()); + // Return cleartext in JSON + DmaapAccess clone = new DmaapAccess(dmaapAccess); + clone.setPassword(clone.decryptPassword()); + clearList.add(clone); + } + + // Create new rows for any configured URLs not found for this user. + for (int i = 0; i < configUrlList.size(); ++i) { + String missUrl = configUrlList.get(i); + logger.debug(EELFLoggerDelegate.debugLogger, "getOrInitDmaapAccessList: adding missing URL {}", missUrl); + DmaapAccess newDmaapAccess = new DmaapAccess(); + // Create a semi-reasonable name for the table + newDmaapAccess.setName("dmaap-" + Integer.toString(i + 1)); + newDmaapAccess.setUserId(userId); + newDmaapAccess.setDmaapUrl(missUrl); + // Write to db. + getDmaapAccessService().saveDmaapAccess(newDmaapAccess); + // Add to response, which assumes the write was successful. + clearList.add(newDmaapAccess); + } + + return clearList; + } + + /** + * Gets the user's selected DMaaP access profile. + * + * @param userId + * User's UID + * @return DmaapAccess object that is currently selected, or the first one + * found if none are selected; null if no access profiles are + * configured. + * @throws ServletException + * If the user's UID cannot be extracted from request. + */ + protected DmaapAccess getSelectedDmaapAccess(String userId) throws Exception { + List<DmaapAccess> profiles = getOrInitDmaapAccessList(userId); + if (profiles.size() == 0) { + logger.debug("getSelectedDmaapAccess: no rows found, returning null"); + return null; + } + + // Return the first one by default if nothing is selected. + DmaapAccess selected = profiles.get(0); + for (DmaapAccess da : profiles) + if (da.getSelected()) + selected = da; + + return selected; + } + + /** + * Supports sorting a list of feeds by the first column displayed: ID + */ + private static Comparator<DmaapObject> feedComparator = new Comparator<DmaapObject>() { + @Override + public int compare(DmaapObject o1, DmaapObject o2) { + Feed f1 = (Feed) o1; + Feed f2 = (Feed) o2; + // sort these numbers lexicographically, same as the front end + // table. + return f1.getFeedId().compareTo(f2.getFeedId()); + } + }; + + /** + * Supports sorting a list of publishers by the first column displayed: pub + * ID + */ + private static Comparator<DmaapObject> pubComparator = new Comparator<DmaapObject>() { + @Override + public int compare(DmaapObject o1, DmaapObject o2) { + DR_Pub p1 = (DR_Pub) o1; + DR_Pub p2 = (DR_Pub) o2; + return p1.getPubId().compareTo(p2.getPubId()); + } + }; + + /** + * Supports sorting a list of subscribers by the first column displayed: sub + * ID + */ + private static Comparator<DmaapObject> subComparator = new Comparator<DmaapObject>() { + @Override + public int compare(DmaapObject o1, DmaapObject o2) { + DR_Sub s1 = (DR_Sub) o1; + DR_Sub s2 = (DR_Sub) o2; + // sort these numbers lexicographically, same as the front end + // table. + return s1.getSubId().compareTo(s2.getSubId()); + } + }; + + /** + * Supports sorting a list of topics by the first column displayed: FQTN + */ + private static Comparator<DmaapObject> topicComparator = new Comparator<DmaapObject>() { + @Override + public int compare(DmaapObject o1, DmaapObject o2) { + Topic t1 = (Topic) o1; + Topic t2 = (Topic) o2; + return t1.getFqtn().compareTo(t2.getFqtn()); + } + }; + + /** + * Supports sorting a list of clients by the first column displayed: client + * ID. + */ + private static Comparator<DmaapObject> clientComparator = new Comparator<DmaapObject>() { + @Override + public int compare(DmaapObject o1, DmaapObject o2) { + MR_Client c1 = (MR_Client) o1; + MR_Client c2 = (MR_Client) o2; + // sort these numbers lexicographically, same as the front end + // table. + return c1.getMrClientId().compareTo(c2.getMrClientId()); + } + }; + + /** + * Gets one page of DMaaP objects and supporting information via the Bus + * Controller REST client. On success, returns a JSON object as String with + * the following tags: + * <UL> + * <LI>status: Integer; HTTP status code 200. + * <LI>dmaapName: String, name returned by the remote DMaaP instance. + * <LI>dcaeLocations: Array of string, locations returned by the remote + * DMaaP instance. + * <LI>data: Array of the desired items; e.g., data router feeds. + * <LI>totalPages: Integer, the number of pages required to display the + * complete list of items using the submitted page size + * </UL> + * + * This duplicates all of {@link #buildJsonSuccess(int, Object)}. + * + * @param dmaapAccess + * Access details for the DMaaP REST API + * @param option + * Specifies which item list type to get: data router feeds, etc. + * @param pageNum + * Page number of results + * @param viewPerPage + * Number of items per page + * @return JSON block as String, see above. + * @throws Exception + */ + private String getItemListForPage(DmaapAccess dmaapAccess, DmaapDataItem option, int pageNum, int viewPerPage) + throws Exception { + DmaapBcRestClient restClient = getDmaapBcRestClient(dmaapAccess); + // Get the instance so the page can display its name + DmaapObject dmaap = restClient.getDmaap(); + if (dmaap instanceof ErrorResponse) { + // Bad password is caught here. + ErrorResponse err = (ErrorResponse) dmaap; + throw new Exception(err.getMessage()); + } + // Get locations for editing + List<DmaapObject> dcaeLocations = restClient.getDcaeLocations(); + if (dcaeLocations.size() == 1 && dcaeLocations.get(0) instanceof ErrorResponse) { + // Should never happen - bad password is caught right above - but be + // careful. + ErrorResponse err = (ErrorResponse) dcaeLocations.get(0); + throw new Exception(err.getMessage()); + } + // Pass them back as String array + String[] dcaeLocs = new String[dcaeLocations.size()]; + for (int i = 0; i < dcaeLocs.length; ++i) { + DcaeLocation dcaeLoc = (DcaeLocation) dcaeLocations.get(i); + dcaeLocs[i] = dcaeLoc.getDcaeLocationName(); + } + // Get the requested item list + List<DmaapObject> itemList = null; + switch (option) { + case DR_FEED: + itemList = restClient.getFeeds(); + Collections.sort(itemList, feedComparator); + break; + case DR_PUB: + itemList = restClient.getDRPubs(); + Collections.sort(itemList, pubComparator); + break; + case DR_SUB: + itemList = restClient.getDRSubs(); + Collections.sort(itemList, subComparator); + break; + case MR_TOPIC: + itemList = restClient.getTopics(); + Collections.sort(itemList, topicComparator); + break; + case MR_CLIENT: + itemList = restClient.getMRClients(); + Collections.sort(itemList, clientComparator); + break; + default: + throw new Exception("getItemListForPage: pgmr error, unimplemented case: " + option.name()); + } + + logger.debug("getItemListForPage: list size is {}", itemList.size()); + int pageCount = (int) Math.ceil((double) itemList.size() / viewPerPage); + @SuppressWarnings("unchecked") + List<DmaapObject> subList = shrinkListToPage(pageNum, viewPerPage, itemList); + itemList = subList; + // Build response here + Map<String, Object> model = new HashMap<String, Object>(); + model.put(STATUS_RESPONSE_KEY, new Integer(200)); + model.put(PROFILE_NAME_RESPONSE_KEY, dmaapAccess.getName()); + model.put(DMAAP_NAME_RESPONSE_KEY, ((Dmaap) dmaap).getDmaapName()); + model.put(DCAE_LOCATIONS_RESPONSE_KEY, dcaeLocs); + model.put(DATA_RESPONSE_KEY, itemList); + model.put(TOTAL_PAGES_RESPONSE_KEY, pageCount); + + // build the response + String outboundJson = null; + try { + outboundJson = mapper.writeValueAsString(model); + } catch (Exception ex) { + // should never happen + logger.error("getItemListForPage: failed to serialize model: ", ex); + throw new Exception("sendItemListForPage", ex); + } + + return outboundJson; + } + + /** + * Gets a page of the specified DMaaP items. This method traps errors and + * constructs an appropriate JSON block if an error happens. + * + * See {@link #getItemListForPage(DmaapAccess, DmaapDataItem, int, int)}. + * + * @param request + * Inbound request + * @param option + * DMaaP item type to get + * @return JSON with list of serialized objects, or an error. + */ + protected String getItemListForPageWrapper(HttpServletRequest request, DmaapDataItem option) + throws ServletException { + String outboundJson = null; + try { + User appUser = UserUtils.getUserSession(request); + if (appUser == null || appUser.getLoginId() == null || appUser.getLoginId().length() == 0) + throw new Exception("getItemListForPageWrapper: Failed to get UID"); + DmaapAccess selected = getSelectedDmaapAccess(appUser.getLoginId()); + if (selected == null) // leap into exception handler + throw new Exception("No DMaaP access profiles are configured."); + int pageNum = Integer.parseInt(request.getParameter(PAGE_NUM_QUERY_PARAM)); + int viewPerPage = Integer.parseInt(request.getParameter(VIEW_PER_PAGE_QUERY_PARAM)); + outboundJson = getItemListForPage(selected, option, pageNum, viewPerPage); + } catch (Exception ex) { + outboundJson = buildJsonError(500, "Failed to get DMaaP item type " + option.name(), ex); + } + return outboundJson; + } + + /** + * Adds an item of the specified type with the specified content. Constructs + * an object by deserializing the JSON block, but ignores any ID field that + * is supplied. + * + * On success, returns a JSON block as String with any data returned by the + * REST client. Throws an exception on any failure. + * + * @param dmaapAccess + * Access details for the DMaaP REST API + * @param userId + * The UID of the user making the request + * @param itemType + * DMaaP item type to add + * @param itemContent + * JSON block to deserialize as an object + * @param scAddlStatus + * HTTP status code 200 is always accepted. If this parameter is + * not null, the value is also considered a valid HTTP status + * code on success; e.g., 204. + * @return JSON object with result of the operation + * @throws Exception + * on any problem + */ + private String addDmaapItem(DmaapAccess dmaapAccess, String userId, DmaapDataItem itemType, String itemContent, + Integer scAddlStatus) throws Exception { + DmaapBcRestClient restClient = getDmaapBcRestClient(dmaapAccess); + HttpStatusAndResponse<Object> hsr = null; + switch (itemType) { + case DR_FEED: + Feed feed = mapper.readValue(itemContent, Feed.class); + logger.debug("addDmaapItem: received feed: {} ", feed); + // Null out any ID to get an auto-generated ID + feed.setFeedId(null); + // Assign the owner to be the webapp user + feed.setOwner(userId); + hsr = restClient.postFeed(feed); + break; + case DR_PUB: + DR_Pub pub = mapper.readValue(itemContent, DR_Pub.class); + logger.debug("addDmaapItem: received pub: {} ", pub); + // Null out any ID to get an auto-generated ID + pub.setPubId(null); + hsr = restClient.postDRPub(pub); + break; + case DR_SUB: + DR_Sub sub = mapper.readValue(itemContent, DR_Sub.class); + logger.debug("addDmaapItem: received sub: {} ", sub); + // Null out any ID to get an auto-generated ID + sub.setSubId(null); + // Assign the owner to be the webapp user + sub.setOwner(userId); + hsr = restClient.postDRSub(sub); + break; + case MR_TOPIC: + Topic topic = mapper.readValue(itemContent, Topic.class); + logger.debug("addDmaapItem: received topic: {} ", topic); + // No ID on topic + topic.setOwner(userId); + hsr = restClient.postTopic(topic); + break; + case MR_CLIENT: + MR_Client client = mapper.readValue(itemContent, MR_Client.class); + logger.debug("addDmaapItem: received client: {} ", client); + client.setMrClientId(null); + hsr = restClient.postMRClient(client); + break; + default: + throw new Exception("addDmaapItem: pgmr error, unimplemented case: " + itemType.name()); + } + + // Build result here + String outboundJson = null; + if (hsr.getStatusCode() == HttpServletResponse.SC_OK + || (scAddlStatus != null && hsr.getStatusCode() == scAddlStatus)) { + outboundJson = buildJsonSuccess(hsr.getStatusCode(), hsr.getResponseString()); + } else { + throw new Exception("Unexpected HTTP response code " + Integer.toString(hsr.getStatusCode()) + + " with content " + hsr.getResponseString()); + } + return outboundJson; + } + + /** + * Adds the specified DMaaP item that is read from the request body. This + * method traps errors and constructs an appropriate JSON block if an error + * happens. + * + * @param request + * Used to obtain user info from the active session + * @param itemType + * DMaaP item type to add + * @param scAddlStatus + * HTTP status code 200 is always accepted. If this parameter is + * not null, the value is also considered a valid HTTP status + * code on success; e.g., 204. + * @return JSON block with success or failure object + */ + protected String addItem(HttpServletRequest request, DmaapDataItem itemType, Integer scAddlStatus) + throws ServletException { + String outboundJson = null; + try { + User appUser = UserUtils.getUserSession(request); + if (appUser == null || appUser.getLoginId() == null || appUser.getLoginId().length() == 0) + throw new Exception("addDmaapItem: Failed to get UID"); + + DmaapAccess access = getSelectedDmaapAccess(appUser.getLoginId()); + if (access == null) // leap into exception handler + throw new Exception("No DMaaP access profiles are configured."); + String jsonContent = getBody(request); + outboundJson = addDmaapItem(access, appUser.getLoginId(), itemType, jsonContent, scAddlStatus); + } catch (Exception ex) { + outboundJson = buildJsonError(500, "Failed to add DMaaP item " + itemType.name(), ex); + } + + return outboundJson; + } + + /** + * Updates an item of the specified type with the specified content. + * Constructs an object by deserializing the JSON block. + * + * On success, returns a JSON block as String with any data returned by the + * REST client. Throws an exception on any failure. + * + * @param dmaapAccess + * Access details for the DMaaP REST API + * @param userId + * The UID of the user making the request + * @param itemType + * DMaaP item type to update + * @param itemId + * Item identification + * @param itemContent + * JSON block to deserialize as an object + * @param scAddlStatus + * HTTP status code 200 is always accepted. If this parameter is + * not null, the value is also considered a valid HTTP status + * code on success; e.g., 204. + * @return JSON object with result of the operation + * @throws Exception + * on any problem + */ + private String updateDmaapItem(DmaapAccess dmaapAccess, String userId, DmaapDataItem itemType, String itemId, + String itemContent, Integer scAddlStatus) throws Exception { + DmaapBcRestClient restClient = getDmaapBcRestClient(dmaapAccess); + HttpStatusAndResponse<Object> hsr = null; + switch (itemType) { + case DR_FEED: + Feed feed = mapper.readValue(itemContent, Feed.class); + logger.debug("updateDmaapItem: received feed: {} ", feed); + // Ensure the owner is the webapp user + feed.setOwner(userId); + hsr = restClient.putFeed(feed); + break; + case DR_PUB: + DR_Pub pub = mapper.readValue(itemContent, DR_Pub.class); + logger.debug("updateDmaapItem: received pub: {} ", pub); + hsr = restClient.putDRPub(pub); + break; + case DR_SUB: + DR_Sub sub = mapper.readValue(itemContent, DR_Sub.class); + logger.debug("updateDmaapItem: received sub: {} ", sub); + // Ensure the owner is the webapp user + sub.setOwner(userId); + hsr = restClient.putDRSub(sub); + break; + case MR_TOPIC: + Topic topic = mapper.readValue(itemContent, Topic.class); + logger.debug("updateDmaapItem: received topic: {} ", topic); + // Ensure the owner is the webapp user + topic.setOwner(userId); + if (true && userId != null) + throw new UnsupportedOperationException("put topic"); + // TODO: someday the backend may implement PUT + break; + case MR_CLIENT: + MR_Client client = mapper.readValue(itemContent, MR_Client.class); + logger.debug("updateDmaapItem: received client: {} ", client); + hsr = restClient.putMRClient(client); + break; + default: + throw new Exception("updateDmaapItem: pgmr error, unimplemented case: " + itemType.name()); + } + + // Build result here + String outboundJson = null; + if (hsr.getStatusCode() == HttpServletResponse.SC_OK + || (scAddlStatus != null && hsr.getStatusCode() == scAddlStatus)) { + outboundJson = buildJsonSuccess(hsr.getStatusCode(), hsr.getResponseString()); + } else { + throw new Exception("Unexpected HTTP response code " + Integer.toString(hsr.getStatusCode()) + + " with content " + hsr.getResponseString()); + } + return outboundJson; + } + + /** + * Updates the specified DMaaP item that is read from the request body. This + * method traps errors and constructs an appropriate JSON block if an error + * happens. + * + * @param request + * Used to obtain user info from the active session + * @param itemType + * DMaaP item type to update + * @param itemId + * Item identification to update + * @param scUpdatelStatus + * HTTP status code 200 is always accepted. If this parameter is + * not null, the value is also considered a valid HTTP status + * code on success; e.g., 204. + * @return JSON object with success or error information. + */ + protected String updateItem(HttpServletRequest request, DmaapDataItem itemType, String itemId, + Integer scUpdatelStatus) throws ServletException { + String outboundJson = null; + try { + User appUser = UserUtils.getUserSession(request); + if (appUser == null || appUser.getLoginId() == null || appUser.getLoginId().length() == 0) + throw new Exception("updateItem: Failed to get UID"); + DmaapAccess access = getSelectedDmaapAccess(appUser.getLoginId()); + if (access == null) // leap into exception handler + throw new Exception("No DMaaP access profiles are configured."); + String jsonContent = getBody(request); + outboundJson = updateDmaapItem(access, appUser.getLoginId(), itemType, itemId, jsonContent, + scUpdatelStatus); + } catch (Exception ex) { + outboundJson = buildJsonError(500, "Failed to update DMaaP item " + itemType.name(), ex); + } + + return outboundJson; + } + + /** + * Deletes an item of the specified type with the specified ID. + * + * @param dmaapAccess + * Access details for the DMaaP REST API + * @param itemType + * DMaaP item type to delete + * @param itemId + * Item identification + * @param scAddlStatus + * HTTP status code 200 is always accepted. If this parameter is + * not null, the value is also considered a valid HTTP status + * code on success; e.g., 204. + * @return On success, returns a JSON block as String with any data returned + * by the REST client. + * @throws Exception + * Throws an exception on any failure. + */ + private String deleteDmaapItem(DmaapAccess dmaapAccess, DmaapDataItem itemType, String itemId, Integer scAddlStatus) + throws Exception { + DmaapBcRestClient restClient = getDmaapBcRestClient(dmaapAccess); + HttpStatusAndResponse<Object> hsr = null; + switch (itemType) { + case DR_FEED: + hsr = restClient.deleteFeed(itemId); + break; + case DR_PUB: + hsr = restClient.deleteDRPub(itemId); + break; + case DR_SUB: + hsr = restClient.deleteDRSub(itemId); + break; + case MR_TOPIC: + hsr = restClient.deleteTopic(itemId); + break; + case MR_CLIENT: + hsr = restClient.deleteMRClient(itemId); + break; + default: + throw new Exception("deleteDmaapItem: pgmr error, unimplemented case: " + itemType.name()); + } + + // Build result here + String outboundJson = null; + if (hsr.getStatusCode() == HttpServletResponse.SC_OK + || (scAddlStatus != null && hsr.getStatusCode() == scAddlStatus)) { + outboundJson = buildJsonSuccess(hsr.getStatusCode(), hsr.getResponseString()); + } else { + throw new Exception("Unexpected HTTP response code " + Integer.toString(hsr.getStatusCode()) + + " with content " + hsr.getResponseString()); + } + return outboundJson; + } + + /** + * Deletes the specified DMaaP item. This method traps errors and constructs + * an appropriate JSON block if an error happens. + * + * @param request + * Used to obtain user info from the active session + * @param itemType + * DMaaP item type to delete + * @param itemId + * item ID to delete + * @param scAddlStatus + * HTTP status code 200 is always accepted. If this parameter is + * not null, the value is also considered a valid HTTP status + * code on success; e.g., 204. + * @return JSON object with success or error information. + */ + protected String deleteItem(HttpServletRequest request, DmaapDataItem itemType, String itemId, Integer scAddlStatus) + throws ServletException { + String outboundJson = null; + try { + User appUser = UserUtils.getUserSession(request); + if (appUser == null || appUser.getLoginId() == null || appUser.getLoginId().length() == 0) + throw new Exception("deleteItem: Failed to get UID"); + DmaapAccess selected = getSelectedDmaapAccess(appUser.getLoginId()); + if (selected == null) // leap into exception handler + throw new Exception("No DMaaP access profiles are configured."); + outboundJson = deleteDmaapItem(selected, itemType, itemId, scAddlStatus); + } catch (Exception ex) { + outboundJson = buildJsonError(500, "Failed to delete DMaaP item " + itemType.name() + " ID " + itemId, ex); + } + return outboundJson; + } + +} diff --git a/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DmaapAccessController.java b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DmaapAccessController.java new file mode 100644 index 0000000..9b36c8b --- /dev/null +++ b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/DmaapAccessController.java @@ -0,0 +1,374 @@ +/*- + * ================================================================================ + * DCAE DMaaP Bus Controller Web Application + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.dmaapbc.dbcapp.controller; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; + +import org.openecomp.dcae.dmaap.bcapi.client.DmaapBcRestClient; +import org.openecomp.dcae.dmaapbc.model.Dmaap; +import org.openecomp.dcae.dmaapbc.model.DmaapObject; +import org.openecomp.dcae.dmaapbc.model.ErrorResponse; +import org.openecomp.dmaapbc.dbcapp.domain.DmaapAccess; +import org.openecomp.portalsdk.core.domain.User; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.portalsdk.core.web.support.UserUtils; +import org.slf4j.MDC; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * DMaaP Access controller: serves Ajax requests made by Angular on pages where + * the user adds, edits and deletes DMaaP access profiles. This controller must + * defend the database against rogue requests including SQL injection attacks. + */ +@Controller +@RequestMapping("/") +public class DmaapAccessController extends DbcappRestrictedBaseController { + + /** + * Logger that conforms with ECOMP guidelines + */ + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(DmaapAccessController.class); + + private static final String DMAAP_ACCESS_PATH = "/dmaap_access"; + private static final String SELECT_DMAAP_ACCESS_PATH = "/select_dmaap_access"; + + /** + * For general use in these methods + */ + private final ObjectMapper mapper; + + /** + * Never forget that Spring autowires fields AFTER the constructor is + * called. + */ + public DmaapAccessController() { + mapper = new ObjectMapper(); + } + + /** + * Gets a list of DMaaP access profiles for this user from the database and + * returns them in a JSON array nested within a response object. + * + * See {@link #getOrInitDmaapAccessList(String)}. + * + * @param request + * @return JSON with access profiles, or an error JSON if the request fails. + * @throws ServletException + */ + @RequestMapping(value = { DMAAP_ACCESS_PATH }, method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public String getDmaapAccessList(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String outboundJson = null; + try { + User appUser = UserUtils.getUserSession(request); + if (appUser == null || appUser.getLoginId() == null || appUser.getLoginId().length() == 0) + throw new Exception("getDmaapAccessList: Failed to get UID from request"); + List<DmaapAccess> dbAccessList = getOrInitDmaapAccessList(appUser.getLoginId()); + // Wrap the list in the status indicator. + Map<String, Object> model = new HashMap<String, Object>(); + model.put(STATUS_RESPONSE_KEY, new Integer(200)); + model.put(DATA_RESPONSE_KEY, dbAccessList); + outboundJson = mapper.writeValueAsString(model); + } catch (Exception ex) { + outboundJson = buildJsonError(500, "Failed to get DMaaP access profile list", ex); + } + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return outboundJson; + } + + /** + * Adds a DMaaP access profile for the requesting user ID; ignores any + * values for row ID and user ID in the body. + * + * @param request + * @return Trivial JSON object indicating success or failure. + * @throws ServletException + */ + @RequestMapping(value = { DMAAP_ACCESS_PATH }, method = RequestMethod.POST, produces = "application/json") + @ResponseBody + public String addDmaapAccess(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String outboundJson = null; + try { + User appUser = UserUtils.getUserSession(request); + if (appUser == null || appUser.getLoginId() == null || appUser.getLoginId().length() == 0) + throw new Exception("addDmaapAccess: Failed to get UID"); + + DmaapAccess dmaapAccess = mapper.readValue(request.getReader(), DmaapAccess.class); + logger.debug("addDmaapAccess: received object: {} ", dmaapAccess); + + // Null out ID to get an auto-generated ID + dmaapAccess.setId(null); + // Overwrite any submitted user id + dmaapAccess.setUserId(appUser.getLoginId()); + // Encrypt password + if (dmaapAccess.getPassword() != null) + dmaapAccess.encryptPassword(dmaapAccess.getPassword()); + + // Create a new row + getDmaapAccessService().saveDmaapAccess(dmaapAccess); + + // Answer success + outboundJson = buildJsonSuccess(200, null); + } catch (Exception ex) { + outboundJson = buildJsonError(500, "Failed to add DMaaP access profile", ex); + } + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return outboundJson; + } + + /** + * Updates a DMaaP access profile if the row user ID matches the requesting + * user ID. + * + * @param id + * Path parameter with ID of the DMaaP access profile + * @param request + * @return Trivial JSON object indicating success or failure. + * @throws ServletException + * in case of unrecoverable failure + */ + @RequestMapping(value = { DMAAP_ACCESS_PATH + "/{id}" }, method = RequestMethod.PUT, produces = "application/json") + @ResponseBody + public String updateDmaapAccess(@PathVariable("id") long id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String outboundJson = null; + try { + User appUser = UserUtils.getUserSession(request); + if (appUser == null || appUser.getLoginId() == null || appUser.getLoginId().length() == 0) + throw new Exception("updateDmaapAccess: Failed to get UID"); + + DmaapAccess domainObj = getDmaapAccessService().getDmaapAccess(id); + if (!appUser.getLoginId().equals(domainObj.getUserId())) + throw new Exception("updateDmaapAccess: mismatch of appUser and row user ID"); + + DmaapAccess dmaapAccess = mapper.readValue(request.getReader(), DmaapAccess.class); + logger.debug("updateDmaapAccess: received object: {} ", dmaapAccess); + + // Use the path-parameter id; don't trust the one in the object + dmaapAccess.setId(id); + // Encrypt password if present + if (dmaapAccess.getPassword() != null) + dmaapAccess.encryptPassword(dmaapAccess.getPassword()); + + // Update the existing row + getDmaapAccessService().saveDmaapAccess(dmaapAccess); + + // Answer "OK" + outboundJson = buildJsonSuccess(200, null); + } catch (Exception ex) { + outboundJson = buildJsonError(500, "Failed to update DMaaP access profile", ex); + } + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return outboundJson; + } + + /** + * Deletes a DMaaP access profile if the row user ID matches the requesting + * user ID. + * + * @param id + * Path parameter with ID of the DMaaP access profile + * @param request + * @return Trivial JSON object indicating success or failure (altho this is + * slightly contrary to conventions for a DELETE method) + * @throws ServletException + * in case of unrecoverable failure + */ + @RequestMapping(value = { + DMAAP_ACCESS_PATH + "/{id}" }, method = RequestMethod.DELETE, produces = "application/json") + @ResponseBody + public String deleteDmaapAccess(@PathVariable("id") long id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String outboundJson = null; + try { + User appUser = UserUtils.getUserSession(request); + if (appUser == null || appUser.getLoginId() == null || appUser.getLoginId().length() == 0) + throw new Exception("deleteDmaapAccess: Failed to get UID"); + // Validate that this user owns the row with the specified ID + DmaapAccess domainObj = getDmaapAccessService().getDmaapAccess(id); + if (!appUser.getLoginId().equals(domainObj.getUserId())) + throw new Exception("deleteDmaapAccess: mismatch of appUser and row user ID"); + + getDmaapAccessService().deleteDmaapAccess(id); + + // Answer "OK" + outboundJson = buildJsonSuccess(200, null); + } catch (Exception ex) { + outboundJson = buildJsonError(500, "Failed to delete DMaaP access profile", ex); + } + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return outboundJson; + } + + /** + * Gets the selected DMaaP access row for the requesting user. + * + * See {@link #getSelectedDmaapAccess(String)} + * + * @param request + * @return JSON object with one DmaapAccessProfile, or an error + * @throws ServletException + * in case of unrecoverable failure + */ + @RequestMapping(value = { SELECT_DMAAP_ACCESS_PATH }, method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public String getSelectedDmaapAccessProfile(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String outboundJson = null; + try { + User appUser = UserUtils.getUserSession(request); + if (appUser == null || appUser.getLoginId() == null || appUser.getLoginId().length() == 0) + throw new Exception("getSelectedDmaapAccessProfile: Failed to get UID"); + DmaapAccess selected = super.getSelectedDmaapAccess(appUser.getLoginId()); + // clone and decrypt + DmaapAccess clear = new DmaapAccess(selected); + try { + clear.setPassword(clear.decryptPassword()); + } catch (Exception ex) { + throw new ServletException("getSelectedDmaapAccessProfile: Failed to decrypt password", ex); + } + outboundJson = buildJsonSuccess(200, clear); + } catch (Exception ex) { + outboundJson = buildJsonError(500, "Failed to get selected DMaaP access profile", ex); + } + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return outboundJson; + } + + /** + * Marks the DMaaP access row as selected (first choice) for the requesting + * user if the row user ID matches the requesting user ID. As a side effect, + * removes selected marking from all other user rows. Returns status, + * additionally an error message on failure. + * + * Choice of PUT is fairly arbitrary - there is no body, but GET is for + * actions that do not change data. + * + * @param id + * Path parameter with ID of the DMaaP access profile + * @param request + * @return Trivial JSON object indicating success or failure. + * @throws ServletException + * in case of unrecoverable failure + */ + @RequestMapping(value = { + SELECT_DMAAP_ACCESS_PATH + "/{id}" }, method = RequestMethod.PUT, produces = "application/json") + @ResponseBody + public String selectDmaapAccess(@PathVariable("id") long id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String outboundJson = null; + try { + User appUser = UserUtils.getUserSession(request); + if (appUser == null || appUser.getLoginId() == null || appUser.getLoginId().length() == 0) + throw new Exception("selectDmaapAccess: Failed to get UID"); + // A little inefficient in that it requires 3 database accesses; + // probably could be done in 1 with some sophisticated SQL. + List<DmaapAccess> dmaapAccessList = getDmaapAccessService().getDmaapAccessList(appUser.getLoginId()); + for (DmaapAccess dmaap : dmaapAccessList) { + // Only write out the changed rows. + boolean changed = false; + if (id == dmaap.getId()) { + changed = !dmaap.getSelected(); + dmaap.setSelected(true); + } else { + changed = dmaap.getSelected(); + dmaap.setSelected(false); + } + if (changed) + getDmaapAccessService().saveDmaapAccess(dmaap); + } + + // Answer OK + outboundJson = buildJsonSuccess(200, null); + } catch (Exception ex) { + outboundJson = buildJsonError(500, "Failed to select a DMaaP access profile", ex); + } + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return outboundJson; + } + + /** + * Tests the URL in the DMaaP access profile object. + * + * @param request + * @return JSON with a Dmaap object (which has name etc.) on success, error + * on failure. + * @throws ServletException + * in case of unrecoverable failure + */ + @RequestMapping(value = { "test_dmaap_access" }, method = RequestMethod.POST, produces = "application/json") + @ResponseBody + public String testDmaapAccess(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String outboundJson = null; + try { + DmaapAccess dmaapAccess = mapper.readValue(request.getReader(), DmaapAccess.class); + logger.debug("testDmaapAccess: received object: {} ", dmaapAccess); + if (dmaapAccess.getDmaapUrl() == null || dmaapAccess.getDmaapUrl().trim().length() == 0) + throw new Exception("Null or empty URL"); + + DmaapBcRestClient restClient = getDmaapBcRestClient(dmaapAccess); + // Get the instance so the page can display its name + DmaapObject dmaap = restClient.getDmaap(); + if (dmaap instanceof Dmaap) { + outboundJson = buildJsonSuccess(200, dmaap); + } else { + // Bad credentials lands here. + ErrorResponse err = (ErrorResponse) dmaap; + outboundJson = buildJsonError(500, "Test failed: " + err.getMessage(), null); + } + } catch (Exception ex) { + // This is entirely likely; e.e., unknown host exception on typo. + outboundJson = buildJsonError(500, "Invalid DMaaP URL", ex); + } + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return outboundJson; + } + +} diff --git a/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/HealthCheckController.java b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/HealthCheckController.java new file mode 100644 index 0000000..90a67a1 --- /dev/null +++ b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/HealthCheckController.java @@ -0,0 +1,157 @@ +/*- + * ================================================================================ + * DCAE DMaaP Bus Controller Web Application + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.dmaapbc.dbcapp.controller; + +import java.util.Date; + +import javax.servlet.http.HttpServletRequest; + +import org.openecomp.dmaapbc.dbcapp.service.DmaapAccessService; +import org.openecomp.portalsdk.core.controller.UnRestrictedBaseController; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * This controller responds to probes for application health, returning a JSON + * body to indicate current status. + */ +@RestController +@Configuration +@EnableAspectJAutoProxy +@RequestMapping("/") +public class HealthCheckController extends UnRestrictedBaseController { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(HealthCheckController.class); + + private static final String HEALTH_CHECK_PATH = "/healthCheck"; + + @Autowired + private DmaapAccessService dmaapAccessService; + + /** + * Model for JSON response with health-check results. + */ + public class HealthStatus { + // Either 200 or 500 + public int statusCode; + // Additional detail in case of error, empty in case of success. + public String message; + + public HealthStatus(int code, String msg) { + this.statusCode = code; + this.message = msg; + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int code) { + this.statusCode = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String msg) { + this.message = msg; + } + } + + /** + * Checks application health by making a trivial query to the database. + * + * @param request + * @return 200 if database access succeeds, 500 if it fails. + */ + @RequestMapping(value = { HEALTH_CHECK_PATH }, method = RequestMethod.GET, produces = "application/json") + public HealthStatus healthCheck(HttpServletRequest request) { + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + HealthStatus healthStatus = null; + try { + logger.debug(EELFLoggerDelegate.debugLogger, "Performing health check"); + dmaapAccessService.getDmaapAccessCount(); + healthStatus = new HealthStatus(200, "health check succeeded"); + } catch (Exception ex) { + logger.error(EELFLoggerDelegate.errorLogger, "Failed to perform health check", ex); + healthStatus = new HealthStatus(500, "health check failed: " + ex.toString()); + } + return healthStatus; + } + + /** + * This implementation does not support suspend/resume. + * + * @param request + * @return Trivial success + */ + @RequestMapping(value = { + HEALTH_CHECK_PATH + "/suspend" }, method = RequestMethod.GET, produces = "application/json") + public HealthStatus healthCheckSuspend(HttpServletRequest request) { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + HealthStatus response = new HealthStatus(200, "suspend not implemented"); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * This implementation does not support suspend/resume. + * + * @param request + * @return Trivial success + */ + @RequestMapping(value = { + HEALTH_CHECK_PATH + "/resume" }, method = RequestMethod.GET, produces = "application/json") + public HealthStatus healthCheckResume(HttpServletRequest request) { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + HealthStatus response = new HealthStatus(200, "resume not implemented"); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Answers ping request without checking the application health. + * + * @param request + * @return Trivial success + */ + @RequestMapping(value = { HEALTH_CHECK_PATH + "/ping" }, method = RequestMethod.GET, produces = "application/json") + public HealthStatus ping(HttpServletRequest request) { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + HealthStatus response = new HealthStatus(200, "ping received"); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } +} diff --git a/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/ManifestController.java b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/ManifestController.java new file mode 100644 index 0000000..a2986f9 --- /dev/null +++ b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/ManifestController.java @@ -0,0 +1,113 @@ +/*- + * ================================================================================ + * DCAE DMaaP Bus Controller Web Application + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.dmaapbc.dbcapp.controller; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; + +import org.openecomp.portalsdk.core.controller.RestrictedBaseController; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * This controller responds to a request for the web application manifest, + * returning a JSON with the information that was created at build time. + * + * Manifest entries have names with hyphens, which means Javascript code can't + * simply use the shorthand object.key; instead use object['key']. + */ +@RestController +@Configuration +@EnableAspectJAutoProxy +@RequestMapping("/") +public class ManifestController extends RestrictedBaseController { + + private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(ManifestController.class); + + /** + * Required to obtain the webapp manifest. + */ + @Autowired + ServletContext context; + + /** + * Gets the content of the webapp manifest file META-INF/MANIFEST.MF. + * + * @return Attributes object with key-value pairs from the manifest + * @throws IOException + */ + private Attributes getWebappManifest() throws IOException { + // Path to resource on classpath + final String MANIFEST_RESOURCE_PATH = "/META-INF/MANIFEST.MF"; + // Manifest is formatted as Java-style properties + InputStream inputStream = context.getResourceAsStream(MANIFEST_RESOURCE_PATH); + if (inputStream == null) + throw new IOException("getWebappManifest: failed to get resource at path " + MANIFEST_RESOURCE_PATH); + Manifest manifest = new Manifest(inputStream); + inputStream .close(); + return manifest.getMainAttributes(); + } + + /** + * Gets the webapp manifest contents as a JSON object. + * + * @param request + * @return A map of key-value pairs. On success: + * + * <pre> + * { "manifest" : { "key1": "value1", "key2": "value2" } } + * </pre> + * + * On failure: + * + * <pre> + * { "error": "message" } + * </pre> + */ + @RequestMapping(value = { "/manifest" }, method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public Map<String, Object> getManifest(HttpServletRequest request) { + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + Map<String, Object> response = new HashMap<String, Object>(); + try { + Attributes attributes = getWebappManifest(); + response.put("manifest", attributes); + } catch (Exception ex) { + logger.error(EELFLoggerDelegate.errorLogger, "getManifest: failed to read manifest", ex); + response.put("error", "failed to get manifest: " + ex.toString()); + } + return response; + } + +} diff --git a/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/MessageRouterController.java b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/MessageRouterController.java new file mode 100644 index 0000000..1513e17 --- /dev/null +++ b/dcae_dmaapbc_webapp/src/main/java/org/openecomp/dmaapbc/dbcapp/controller/MessageRouterController.java @@ -0,0 +1,226 @@ +/*- + * ================================================================================ + * DCAE DMaaP Bus Controller Web Application + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ================================================================================ + */ +package org.openecomp.dmaapbc.dbcapp.controller; + +import java.util.Date; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.slf4j.MDC; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * Message Router controller: serves Ajax requests made by Angular scripts on + * pages that show topics and clients. + */ +@Controller +@RequestMapping("/") +public class MessageRouterController extends DbcappRestrictedBaseController { + + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MessageRouterController.class); + + private static final String TOPIC_PATH = "/mr_topic"; + private static final String CLIENT_PATH = "/mr_client"; + + public MessageRouterController() { + } + + /** + * Answers a request for one page of message router topics. See + * {@link #getItemListForPageWrapper(HttpServletRequest, DmaapDataItem)} + * + * @param request + * @return Item list for the specified page + * @throws ServletException + */ + @RequestMapping(value = { TOPIC_PATH }, method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public String getMRTopicsByPage(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = getItemListForPageWrapper(request, DmaapDataItem.MR_TOPIC); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Answers a request for one page of message router clients. See + * {@link #getItemListForPageWrapper(HttpServletRequest, DmaapDataItem)} + * + * @param request + * @return Item list for the specified page + * @throws ServletException + */ + @RequestMapping(value = { CLIENT_PATH }, method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public String getMRClientsByPage(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = getItemListForPageWrapper(request, DmaapDataItem.MR_CLIENT); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Adds a topic with the specified information. Expects a JSON block in the + * request body - a Topic object. + * + * @param request + * @return Item list for the specified page + * @throws ServletException + */ + @RequestMapping(value = { TOPIC_PATH }, method = RequestMethod.POST, produces = "application/json") + @ResponseBody + public String addTopic(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = addItem(request, DmaapDataItem.MR_TOPIC, HttpServletResponse.SC_CREATED); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Adds a client with the specified information. Expects a JSON block in the + * request body - a MR_Client object. + * + * @param request + * @return Success / failure JSON + * @throws ServletException + */ + @RequestMapping(value = { CLIENT_PATH }, method = RequestMethod.POST, produces = "application/json") + @ResponseBody + public String addMRClient(HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = addItem(request, DmaapDataItem.MR_CLIENT, HttpServletResponse.SC_CREATED); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Updates a topic with the specified information. Expects a JSON block in + * the request body - a Topic object. + * + * Writes a JSON object as an HTTP response; on success it has a "status" + * and possibly a "data" item; on failure, also has an "error" item. + * + * @param id + * @param request + * @return Success / failure JSON + * @throws ServletException + */ + @RequestMapping(value = { TOPIC_PATH + "/{id}" }, method = RequestMethod.PUT, produces = "application/json") + @ResponseBody + public String updateTopic(@PathVariable("id") long id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = updateItem(request, DmaapDataItem.MR_TOPIC, Long.toString(id), null); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Updates a client with the specified information. Expects a JSON block in + * the request body - a MR_Client object. + * + * Writes a JSON object as an HTTP response; on success it has a "status" + * and possibly a "data" item; on failure, also has an "error" item. + * + * @param id + * + * @param request + * @return Success / failure JSON + * @throws ServletException + */ + @RequestMapping(value = { CLIENT_PATH + "/{id}" }, method = RequestMethod.PUT, produces = "application/json") + @ResponseBody + public String updateMRClient(@PathVariable("id") long id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = updateItem(request, DmaapDataItem.MR_CLIENT, Long.toString(id), null); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Deletes a topic with the FQTN ID specified as a path parameter. + * + * FQTN is a string of dot-separated names. Spring, in its infinite wisdom, + * truncates extensions on dotted path parameters; e.g., "foo.json" becomes + * "foo". Avoid truncation here with the extra ":.+" incantation at the end. + * + * Writes a JSON object as an HTTP response; on success it only has "status" + * item; on failure, also has an "error" item. + * + * @param id + * Path parameter with object ID + * @param request + * @return Success / failure JSON + * @throws ServletException + */ + @RequestMapping(value = { "/mr_topic/{id:.+}" }, method = RequestMethod.DELETE, produces = "application/json") + @ResponseBody + public String deleteTopic(@PathVariable("id") String id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = deleteItem(request, DmaapDataItem.MR_TOPIC, id, 204); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + + /** + * Deletes a client with the mrClientId specified as a path parameter. + * + * Writes a JSON object as an HTTP response; on success it only has "status" + * item; on failure, also has an "error" item. + * + * @param id + * Path parameter with object ID + * @param request + * @return Success / failure JSON + * @throws ServletException + */ + @RequestMapping(value = { "/mr_client/{id}" }, method = RequestMethod.DELETE, produces = "application/json") + @ResponseBody + public String deleteMRClient(@PathVariable("id") long id, HttpServletRequest request) throws ServletException { + MDC.put(SystemProperties.AUDITLOG_BEGIN_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.setRequestBasedDefaultsIntoGlobalLoggingContext(request, DataBusHomeController.APP_NAME); + String response = deleteItem(request, DmaapDataItem.MR_CLIENT, Long.toString(id), null); + MDC.put(SystemProperties.AUDITLOG_END_TIMESTAMP, DataBusHomeController.logDateFormat.format(new Date())); + logger.info(EELFLoggerDelegate.auditLogger, request.getMethod() + request.getRequestURI()); + return response; + } + +} |