DCAE-D fe initial commit
DCAE-D fe initial commit
Change-Id: Ica8ccb7c7ef769c969664d1e168d205eb9fc67f2
Issue-ID: SDC-1218
Signed-off-by: Stone, Avi (as206k) <as206k@att.com>
diff --git a/src/main/java/org/onap/sdc/dcae/FeApp.java b/src/main/java/org/onap/sdc/dcae/FeApp.java
new file mode 100644
index 0000000..01d757c
--- /dev/null
+++ b/src/main/java/org/onap/sdc/dcae/FeApp.java
@@ -0,0 +1,73 @@
+package org.onap.sdc.dcae;
+
+import org.onap.sdc.dcae.controller.proxy.DcaeProxy;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.boot.web.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.*;
+
+import javax.servlet.ServletContext;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+
+@Configuration
+@ComponentScan()
+@EnableAutoConfiguration
+@PropertySource("file:${jetty.base}/config/dcae-fe/application.properties")
+public class FeApp extends SpringBootServletInitializer implements CommandLineRunner{
+
+ private static final String SPECIFICATION_VERSION = "Specification-Version";
+ @Autowired
+ ServletContext servletContext;
+
+ private static final String MANIFEST_FILE_NAME = "/META-INF/MANIFEST.MF";
+ private static String dcaeVersion;
+
+ @Value("${beUrl}")
+ private String beUrl;
+
+ public static void main(String[] args) {
+ SpringApplication.run(FeApp.class, args);
+ }
+
+ public void run(String... arg0) throws Exception {
+ InputStream inputStream = servletContext.getResourceAsStream(MANIFEST_FILE_NAME);
+
+ System.out.println("Server is starting..reading DCAE version...");
+
+ String version = null;
+ try {
+ Manifest mf = new Manifest(inputStream);
+ Attributes atts = mf.getMainAttributes();
+ version = atts.getValue(SPECIFICATION_VERSION);
+ if (version == null || version.isEmpty()) {
+ System.err.println("failed to read DCAE version from MANIFEST.");
+ } else {
+ System.out.println("DCAE version from MANIFEST is "+ version);
+ dcaeVersion = version;
+ }
+
+ } catch (IOException e) {
+ System.err.println("failed to read DCAE version from MANIFEST: "+ e.getMessage());
+ }
+ }
+
+ public static String getDcaeVersion() {
+ return dcaeVersion;
+ }
+
+
+ @Bean
+ public ServletRegistrationBean dcaeProxyBean() {
+ ServletRegistrationBean bean = new ServletRegistrationBean(new DcaeProxy(beUrl), "/dcaeProxy/*");
+ bean.setLoadOnStartup(1);
+ return bean;
+ }
+}
diff --git a/src/main/java/org/onap/sdc/dcae/controller/health/HealthController.java b/src/main/java/org/onap/sdc/dcae/controller/health/HealthController.java
new file mode 100644
index 0000000..1ddec2d
--- /dev/null
+++ b/src/main/java/org/onap/sdc/dcae/controller/health/HealthController.java
@@ -0,0 +1,72 @@
+package org.onap.sdc.dcae.controller.health;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onap.sdc.dcae.FeApp;
+import org.onap.sdc.dcae.composition.restmodels.health.ComponentsInfo;
+import org.onap.sdc.dcae.composition.restmodels.health.HealthResponse;
+import org.onap.sdc.dcae.composition.util.DcaeFeConstants;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.gson.Gson;
+
+
+/**
+ *
+ * @author lezer
+ * Example:
+ * {
+ "healthCheckComponent": "DCAE Designer",
+ "healthCheckStatus": "UP",
+ "description": "OK",
+ "componentsInfo": [{
+ "healthCheckComponent": "FE",
+ "healthCheckStatus": "UP",
+ "description": "OK"
+ }]
+}
+ *
+ */
+@RestController
+@EnableAutoConfiguration
+@CrossOrigin
+public class HealthController {
+ Gson gson = new Gson();
+
+ @RequestMapping(value = "/healthCheck", method = RequestMethod.GET)
+ public ResponseEntity<String> healthCheck() {
+ try{
+ HealthResponse healthResponse = new HealthResponse();
+ healthResponse.setHealthCheckComponent(DcaeFeConstants.Health.APP_NAME);
+ healthResponse.setHealthCheckStatus(DcaeFeConstants.Health.UP);
+ healthResponse.setSdcVersion(FeApp.getDcaeVersion());
+ healthResponse.setDescription(DcaeFeConstants.Health.OK);
+
+ List<ComponentsInfo> componentsInfoList = new ArrayList<ComponentsInfo>();
+ ComponentsInfo componentsInfo = new ComponentsInfo();
+ componentsInfo.setHealthCheckComponent(DcaeFeConstants.Health.FE);
+ componentsInfo.setHealthCheckStatus(DcaeFeConstants.Health.UP);
+ componentsInfo.setVersion(FeApp.getDcaeVersion());
+ componentsInfo.setDescription(DcaeFeConstants.Health.OK);
+ componentsInfoList.add(componentsInfo);
+
+ healthResponse.setComponentsInfo(componentsInfoList);
+ String json = gson.toJson(healthResponse, HealthResponse.class);
+ System.out.println("Health Check response: "+json);
+
+ return new ResponseEntity<String>(json, HttpStatus.OK);
+ }
+ catch(Exception e){
+ System.err.println("Error occured while performing HealthCheck: "+e.getLocalizedMessage());
+ return new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+}
diff --git a/src/main/java/org/onap/sdc/dcae/controller/proxy/DcaeProxy.java b/src/main/java/org/onap/sdc/dcae/controller/proxy/DcaeProxy.java
new file mode 100644
index 0000000..94279f2
--- /dev/null
+++ b/src/main/java/org/onap/sdc/dcae/controller/proxy/DcaeProxy.java
@@ -0,0 +1,152 @@
+package org.onap.sdc.dcae.controller.proxy;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.proxy.ProxyServlet;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.concurrent.TimeUnit;
+
+
+public class DcaeProxy extends ProxyServlet {
+
+ private static Logger log = LoggerFactory.getLogger(DcaeProxy.class);
+ private static Cache<String, MdcData> mdcDataCache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).build();
+
+
+ //TODO proper configuration class
+ private String beHostUrl;
+
+ public DcaeProxy(String beHostUrl){
+ this.beHostUrl = beHostUrl;
+ }
+
+ @Override
+ protected HttpClient newHttpClient() {
+ SslContextFactory factory = new SslContextFactory(true);
+ return new HttpClient(factory);
+ }
+
+ @Override
+ protected String rewriteTarget(HttpServletRequest request) {
+ try{
+ logRequest(request);
+ }catch (Exception e){
+ log.error("Unexpected FE request logging error :", e);
+ }
+ String uri = request.getRequestURI();
+ uri = uri.replace("/dcaeProxy", "");
+ String query = request.getQueryString();
+ StringBuilder url = new StringBuilder();
+ url.append(beHostUrl).append(uri);
+ if(null != query)
+ url.append("?").append(query);
+ String urlString = url.toString();
+ log.info("Proxy outgoing request={}", urlString);
+ return urlString;
+ }
+
+ @Override
+ protected void onProxyResponseSuccess(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Response serverResponse) {
+ try{
+ logResponse(clientRequest, serverResponse);
+ }catch (Exception e){
+ log.error("Unexpected FE response logging error :", e);
+ }
+ super.onProxyResponseSuccess(clientRequest, proxyResponse, serverResponse);
+ }
+
+
+ private void logRequest(HttpServletRequest httpRequest) {
+ MDC.clear();
+ Long transactionStartTime = System.currentTimeMillis();
+ String requestId = httpRequest.getHeader("X-ECOMP-RequestID");
+ String serviceInstanceID = httpRequest.getHeader("X-ECOMP-ServiceID");
+ if (!StringUtils.isEmpty(requestId)) {
+ String userId = httpRequest.getHeader("USER_ID");
+ String remoteAddr = httpRequest.getRemoteAddr();
+ String localAddr = httpRequest.getLocalAddr();
+ mdcDataCache.put(requestId, new MdcData(serviceInstanceID, userId, remoteAddr, localAddr, transactionStartTime));
+ updateMdc(requestId, serviceInstanceID, userId, remoteAddr, localAddr, null);
+ }
+ inHttpRequest(httpRequest);
+ }
+
+ private void logResponse(HttpServletRequest request, Response proxyResponse) {
+ String requestId = request.getHeader("X-ECOMP-RequestID");
+ if (requestId != null) {
+ MdcData mdcData = mdcDataCache.getIfPresent(requestId);
+ if (mdcData != null) {
+ Long transactionStartTime = mdcData.getTransactionStartTime();
+ String transactionRoundTime = Long.toString(System.currentTimeMillis() - transactionStartTime);
+ updateMdc(requestId, mdcData.getServiceInstanceID(), mdcData.getUserId(), mdcData.getRemoteAddr(), mdcData.getLocalAddr(), transactionRoundTime);
+ }
+ }
+ outHttpResponse(proxyResponse);
+ MDC.clear();
+ }
+
+ private class MdcData {
+ private String serviceInstanceID;
+ private String userId;
+ private String remoteAddr;
+ private String localAddr;
+ private Long transactionStartTime;
+
+ MdcData(String serviceInstanceID, String userId, String remoteAddr, String localAddr, Long transactionStartTime) {
+ this.serviceInstanceID = serviceInstanceID;
+ this.userId = userId;
+ this.remoteAddr = remoteAddr;
+ this.localAddr = localAddr;
+ this.transactionStartTime = transactionStartTime;
+ }
+
+ Long getTransactionStartTime() {
+ return transactionStartTime;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ String getRemoteAddr() {
+ return remoteAddr;
+ }
+
+ String getLocalAddr() {
+ return localAddr;
+ }
+
+ String getServiceInstanceID() {
+ return serviceInstanceID;
+ }
+ }
+
+ private void updateMdc(String uuid, String serviceInstanceID, String userId, String remoteAddr, String localAddr, String transactionStartTime) {
+ MDC.put("uuid", uuid);
+ MDC.put("serviceInstanceID", serviceInstanceID);
+ MDC.put("userId", userId);
+ MDC.put("remoteAddr", remoteAddr);
+ MDC.put("localAddr", localAddr);
+ MDC.put("timer", transactionStartTime);
+ }
+
+ // Extracted for purpose of clear method name, for logback %M parameter
+ private void inHttpRequest(HttpServletRequest httpRequest) {
+ log.info("{} {} {}", httpRequest.getMethod(), httpRequest.getRequestURI(), httpRequest.getProtocol());
+ }
+
+ // Extracted for purpose of clear method name, for logback %M parameter
+ private void outHttpResponse(Response proxyResponse) {
+ log.info("SC=\"{}\"", proxyResponse.getStatus());
+ }
+
+}
diff --git a/src/main/webapp/META-INF/MANIFEST.MF b/src/main/webapp/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..1902117
--- /dev/null
+++ b/src/main/webapp/META-INF/MANIFEST.MF
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0
+Implementation-Title: dcae-fe
+Implementation-Version: APPLICATION_VERSION
+Implementation-Vendor-Id: com.att.tlv.sdc.dcae
+Build-Jdk: 1.7.0_45
+Created-By: Apache Maven 3.2.1
+Archiver-Version: Plexus Archiver
+Specification-Version: 1707.0.0-SNAPSHOT
diff --git a/src/main/webapp/WEB-INF/config/dcae-fe/application.properties b/src/main/webapp/WEB-INF/config/dcae-fe/application.properties
new file mode 100644
index 0000000..a018abb
--- /dev/null
+++ b/src/main/webapp/WEB-INF/config/dcae-fe/application.properties
@@ -0,0 +1,3 @@
+server.port=8181
+server.context-path=/dcae
+beUrl=https://zldcrdm2sdc2abe01.3f1a87.rdm2.tci.att.com:8443
diff --git a/src/main/webapp/WEB-INF/jetty-web.xml b/src/main/webapp/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000..67a135a
--- /dev/null
+++ b/src/main/webapp/WEB-INF/jetty-web.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC
+ "-//Mort Bay Consulting//DTD Configure//EN"
+ "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+ <Set name="contextPath">/dcae</Set>
+</Configure>
diff --git a/src/main/webapp/WEB-INF/rewrite.config b/src/main/webapp/WEB-INF/rewrite.config
new file mode 100644
index 0000000..90893bb
--- /dev/null
+++ b/src/main/webapp/WEB-INF/rewrite.config
@@ -0,0 +1,2 @@
+RewriteRule ^.*\..*$ - [L]
+RewriteRule ^.*$ /index.html [L]
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..8ba3dc1
--- /dev/null
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <display-name>DCAE_FE</display-name>
+
+
+</web-app>
diff --git a/src/test/org/onap/sdc/dcae/controller/proxy/DcaeProxyTest.java b/src/test/org/onap/sdc/dcae/controller/proxy/DcaeProxyTest.java
new file mode 100644
index 0000000..94cb2c0
--- /dev/null
+++ b/src/test/org/onap/sdc/dcae/controller/proxy/DcaeProxyTest.java
@@ -0,0 +1,29 @@
+package org.onap.sdc.dcae.controller.proxy;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+
+//TODO headers and cookies test (API)
+public class DcaeProxyTest {
+
+ private static final String BEHOST = "https://host.xxx.yyy:8443";
+ private DcaeProxy proxy = new DcaeProxy(BEHOST);
+ private final static HttpServletRequest servletRequest = Mockito.mock(HttpServletRequest.class);
+
+
+ @Test
+ public void testRewriteUrlWithQueryParams(){
+ String requestUrl = "/dcae/dcaeProxy/someBeApi?%20x=1&y=2";
+ String expectedUrl = BEHOST + "/dcae/someBeApi?%20x=1&y=2";
+ when(servletRequest.getRequestURI()).thenReturn(requestUrl);
+ String target = proxy.rewriteTarget(servletRequest);
+ assertTrue(target.equals(expectedUrl));
+ }
+
+}