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));
+    }
+
+}