summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorYoung Gyu Park <younggyu@google.com>2018-02-27 12:32:50 +0900
committerYoung Gyu Park <younggyu@google.com>2018-03-12 17:49:54 +0900
commit0af022a827becb32bb6132bd505efffd350de242 (patch)
tree92664185a61da856c180c36855ed970bdf115afc /src/main
parent6a63048585642a823cc7d0a339146e6cdd670277 (diff)
downloaddashboard-0af022a827becb32bb6132bd505efffd350de242.tar.gz
GCS zip log file viewer by unarchiving the file.
Test: Tested with browser on GAE(go/vts-web-staging/show_gcs_log) Bug: 73900965 Change-Id: I65d2ea816a8327e333f2e818e2af19a0989e6b63
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/com/android/vts/servlet/BaseServlet.java2
-rw-r--r--src/main/java/com/android/vts/servlet/ShowGcsLogServlet.java212
-rw-r--r--src/main/webapp/WEB-INF/appengine-web.xml1
-rw-r--r--src/main/webapp/WEB-INF/jsp/error_msg.jsp38
-rw-r--r--src/main/webapp/WEB-INF/jsp/show_gcs_log.jsp73
5 files changed, 260 insertions, 66 deletions
diff --git a/src/main/java/com/android/vts/servlet/BaseServlet.java b/src/main/java/com/android/vts/servlet/BaseServlet.java
index a20cd62..93652b4 100644
--- a/src/main/java/com/android/vts/servlet/BaseServlet.java
+++ b/src/main/java/com/android/vts/servlet/BaseServlet.java
@@ -36,6 +36,8 @@ import javax.servlet.http.HttpSession;
public abstract class BaseServlet extends HttpServlet {
protected final Logger logger = Logger.getLogger(getClass().getName());
+ protected String ERROR_MESSAGE_JSP = "WEB-INF/jsp/error_msg.jsp";
+
// Environment variables
protected static final String GERRIT_URI = System.getProperty("GERRIT_URI");
protected static final String GERRIT_SCOPE = System.getProperty("GERRIT_SCOPE");
diff --git a/src/main/java/com/android/vts/servlet/ShowGcsLogServlet.java b/src/main/java/com/android/vts/servlet/ShowGcsLogServlet.java
index 337494f..583803d 100644
--- a/src/main/java/com/android/vts/servlet/ShowGcsLogServlet.java
+++ b/src/main/java/com/android/vts/servlet/ShowGcsLogServlet.java
@@ -16,25 +16,33 @@
package com.android.vts.servlet;
+import com.google.appengine.api.memcache.ErrorHandlers;
+import com.google.appengine.api.memcache.MemcacheService;
+import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.Bucket;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.Storage.BlobListOption;
import com.google.cloud.storage.StorageOptions;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.logging.Level;
+import com.google.gson.Gson;
+import org.apache.commons.io.IOUtils;
+
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
/**
* A GCS log servlet read log zip file from Google Cloud Storage bucket and show the content in it
@@ -45,36 +53,47 @@ public class ShowGcsLogServlet extends BaseServlet {
private static final String GCS_LOG_JSP = "WEB-INF/jsp/show_gcs_log.jsp";
+ /** Google Cloud Storage project ID */
private static final String GCS_PROJECT_ID = System.getProperty("GCS_PROJECT_ID");
+ /** Google Cloud Storage project's key file to access the storage */
private static final String GCS_KEY_FILE = System.getProperty("GCS_KEY_FILE");
+ /** Google Cloud Storage project's default bucket name for vtslab log files */
private static final String GCS_BUCKET_NAME = System.getProperty("GCS_BUCKET_NAME");
/**
* This is the key file to access vtslab-gcs project. It will allow the dashboard to have a full
- * controll of the bucket.
+ * control of the bucket.
*/
private InputStream keyFileInputStream;
/** This is the instance of java google storage library */
private Storage storage;
+ /** This is the instance of App Engine memcache service java library */
+ private MemcacheService syncCache = MemcacheServiceFactory.getMemcacheService();
+
@Override
public void init(ServletConfig cfg) throws ServletException {
super.init(cfg);
keyFileInputStream =
this.getServletContext().getResourceAsStream("/WEB-INF/keys/" + GCS_KEY_FILE);
- try {
- storage =
- StorageOptions.newBuilder()
- .setProjectId(GCS_PROJECT_ID)
- .setCredentials(
- ServiceAccountCredentials.fromStream(keyFileInputStream))
- .build()
- .getService();
- } catch (IOException e) {
- logger.log(Level.SEVERE, "Error on creating storage instance!");
+ if (keyFileInputStream == null) {
+ logger.log(Level.SEVERE, "Error GCS key file is not exiting. Check key file!");
+ } else {
+ try {
+ storage =
+ StorageOptions.newBuilder()
+ .setProjectId(GCS_PROJECT_ID)
+ .setCredentials(
+ ServiceAccountCredentials.fromStream(keyFileInputStream))
+ .build()
+ .getService();
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, "Error on creating storage instance!");
+ }
}
+ syncCache.setErrorHandler(ErrorHandlers.getConsistentLogAndContinue(Level.INFO));
}
@Override
@@ -90,60 +109,127 @@ public class ShowGcsLogServlet extends BaseServlet {
@Override
public void doGetHandler(HttpServletRequest request, HttpServletResponse response)
throws IOException {
+ if (keyFileInputStream == null) {
+ request.setAttribute("error_title", "GCS Key file Error");
+ request.setAttribute("error_message", "The GCS Key file is not existed!");
+ RequestDispatcher dispatcher = request.getRequestDispatcher(ERROR_MESSAGE_JSP);
+ try {
+ dispatcher.forward(request, response);
+ } catch (ServletException e) {
+ logger.log(Level.SEVERE, "Servlet Excpetion caught : ", e);
+ }
+ } else {
- String path = request.getParameter("path") == null ? "" : request.getParameter("path");
- Path pathInfo = Paths.get(path);
-
- Bucket vtsReportBucket = storage.get(GCS_BUCKET_NAME);
+ String action =
+ request.getParameter("action") == null
+ ? "read"
+ : request.getParameter("action");
+ String path = request.getParameter("path") == null ? "/" : request.getParameter("path");
+ String entry =
+ request.getParameter("entry") == null ? "" : request.getParameter("entry");
+ Path pathInfo = Paths.get(path);
+
+ Bucket vtsReportBucket = storage.get(GCS_BUCKET_NAME);
+
+ List<String> dirList = new ArrayList<>();
+ List<String> fileList = new ArrayList<>();
+ List<String> entryList = new ArrayList<>();
+ Map<String, Object> resultMap = new HashMap<>();
+ String entryContent = "";
+
+ if (pathInfo.toString().endsWith(".zip")) {
+
+ Blob blobFile = (Blob) this.syncCache.get(path.toString());
+ if (blobFile == null) {
+ blobFile = vtsReportBucket.get(path);
+ this.syncCache.put(path.toString(), blobFile);
+ }
- List<String> dirList = new ArrayList<>();
- List<String> fileList = new ArrayList<>();
- if (pathInfo.endsWith(".zip")) {
- Blob blobFile = vtsReportBucket.get(path);
- } else {
+ if (action.equalsIgnoreCase("read")) {
+ InputStream blobInputStream = new ByteArrayInputStream(blobFile.getContent());
+ ZipInputStream zipInputStream = new ZipInputStream(blobInputStream);
+
+ ZipEntry zipEntry;
+ while ((zipEntry = zipInputStream.getNextEntry()) != null) {
+ if (zipEntry.isDirectory()) {
+
+ } else {
+ if (entry.length() > 0) {
+ System.out.println("param entry => " + entry);
+ if (zipEntry.getName().equals(entry)) {
+ System.out.println("matched !!!! " + zipEntry.getName());
+ entryContent =
+ IOUtils.toString(
+ zipInputStream, StandardCharsets.UTF_8.name());
+ }
+ } else {
+ entryList.add(zipEntry.getName());
+ }
+ }
+ }
+ resultMap.put("entryList", entryList);
+ resultMap.put("entryContent", entryContent);
+
+ String json = new Gson().toJson(resultMap);
+ response.setContentType("application/json");
+ response.setCharacterEncoding("UTF-8");
+ response.getWriter().write(json);
+ } else {
+ response.setContentType("application/octet-stream");
+ response.setContentLength(blobFile.getSize().intValue());
+ response.setHeader(
+ "Content-Disposition",
+ "attachment; filename=\"" + pathInfo.getFileName() + "\"");
- logger.log(Level.INFO, "path info => " + pathInfo);
- logger.log(Level.INFO, "path name count => " + pathInfo.getNameCount());
+ response.getOutputStream().write(blobFile.getContent());
+ }
- BlobListOption[] listOptions;
- if (pathInfo.getNameCount() == 0) {
- listOptions = new BlobListOption[] {BlobListOption.currentDirectory()};
} else {
- if (pathInfo.getNameCount() <= 1) {
- dirList.add("/");
+
+ logger.log(Level.INFO, "path info => " + pathInfo);
+ logger.log(Level.INFO, "path name count => " + pathInfo.getNameCount());
+
+ BlobListOption[] listOptions;
+ if (pathInfo.getNameCount() == 0) {
+ listOptions = new BlobListOption[] {BlobListOption.currentDirectory()};
} else {
- dirList.add(pathInfo.getParent().toString());
+ if (pathInfo.getNameCount() <= 1) {
+ dirList.add("/");
+ } else {
+ dirList.add(pathInfo.getParent().toString());
+ }
+ listOptions =
+ new BlobListOption[] {
+ BlobListOption.currentDirectory(),
+ BlobListOption.prefix(pathInfo.toString() + "/")
+ };
}
- listOptions =
- new BlobListOption[] {
- BlobListOption.currentDirectory(),
- BlobListOption.prefix(pathInfo.toString() + "/")
- };
- }
- Iterator<Blob> blobIterator = vtsReportBucket.list(listOptions).iterateAll();
- while (blobIterator.hasNext()) {
- Blob blob = blobIterator.next();
- logger.log(Level.INFO, "blob name => " + blob);
- if (blob.isDirectory()) {
- logger.log(Level.INFO, "directory name => " + blob.getName());
- dirList.add(blob.getName());
- } else {
- logger.log(Level.INFO, "file name => " + blob.getName());
- fileList.add(blob.getName());
+ Iterator<Blob> blobIterator = vtsReportBucket.list(listOptions).iterateAll();
+ while (blobIterator.hasNext()) {
+ Blob blob = blobIterator.next();
+ logger.log(Level.INFO, "blob name => " + blob);
+ if (blob.isDirectory()) {
+ logger.log(Level.INFO, "directory name => " + blob.getName());
+ dirList.add(blob.getName());
+ } else {
+ logger.log(Level.INFO, "file name => " + blob.getName());
+ fileList.add(blob.getName());
+ }
}
- }
- }
- response.setStatus(HttpServletResponse.SC_OK);
- request.setAttribute("dirList", dirList);
- request.setAttribute("fileList", fileList);
- request.setAttribute("path", path);
- RequestDispatcher dispatcher = request.getRequestDispatcher(GCS_LOG_JSP);
- try {
- dispatcher.forward(request, response);
- } catch (ServletException e) {
- logger.log(Level.SEVERE, "Servlet Excpetion caught : ", e);
+ response.setStatus(HttpServletResponse.SC_OK);
+ request.setAttribute("entryList", entryList);
+ request.setAttribute("dirList", dirList);
+ request.setAttribute("fileList", fileList);
+ request.setAttribute("path", path);
+ RequestDispatcher dispatcher = request.getRequestDispatcher(GCS_LOG_JSP);
+ try {
+ dispatcher.forward(request, response);
+ } catch (ServletException e) {
+ logger.log(Level.SEVERE, "Servlet Excpetion caught : ", e);
+ }
+ }
}
}
}
diff --git a/src/main/webapp/WEB-INF/appengine-web.xml b/src/main/webapp/WEB-INF/appengine-web.xml
index 4023fb1..fed561d 100644
--- a/src/main/webapp/WEB-INF/appengine-web.xml
+++ b/src/main/webapp/WEB-INF/appengine-web.xml
@@ -16,6 +16,7 @@
<threadsafe>true</threadsafe>
<sessions-enabled>true</sessions-enabled>
<runtime>java8</runtime>
+ <instance-class>F4_1G</instance-class>
<system-properties>
<property name="EMAIL_DOMAIN" value="${appengine.emailDomain}" />
diff --git a/src/main/webapp/WEB-INF/jsp/error_msg.jsp b/src/main/webapp/WEB-INF/jsp/error_msg.jsp
new file mode 100644
index 0000000..13e6657
--- /dev/null
+++ b/src/main/webapp/WEB-INF/jsp/error_msg.jsp
@@ -0,0 +1,38 @@
+<%--
+ ~ Copyright (c) 2018 Google Inc. All Rights Reserved.
+ ~
+ ~ 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.
+ --%>
+<%@ page contentType='text/html;charset=UTF-8' language='java' %>
+<%@ taglib prefix='fn' uri='http://java.sun.com/jsp/jstl/functions' %>
+<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%>
+
+<html>
+<link rel='stylesheet' href='/css/dashboard_main.css'>
+<%@ include file='header.jsp' %>
+<body>
+
+<div class='container wide'>
+
+ <div class="card-panel">
+ <span class="red-text text-darken-2">
+ <h3> <c:out value="${error_title}"></c:out> </h3>
+ <br/>
+ <c:out value="${error_message}"></c:out>
+ </span>
+ </div>
+
+</div>
+<%@ include file='footer.jsp' %>
+</body>
+</html>
diff --git a/src/main/webapp/WEB-INF/jsp/show_gcs_log.jsp b/src/main/webapp/WEB-INF/jsp/show_gcs_log.jsp
index 5c175ea..8bdf416 100644
--- a/src/main/webapp/WEB-INF/jsp/show_gcs_log.jsp
+++ b/src/main/webapp/WEB-INF/jsp/show_gcs_log.jsp
@@ -27,9 +27,42 @@
<script src='js/plan_runs.js'></script>
<script src='js/search_header.js'></script>
<script type='text/javascript'>
- var search;
$(document).ready(function() {
-
+ var paraMap = {'path': '', 'entry': ''};
+ $('.modal').modal({
+ dismissible: true, // Modal can be dismissed by clicking outside of the modal
+ opacity: .99, // Opacity of modal background
+ inDuration: 300, // Transition in duration
+ outDuration: 200, // Transition out duration
+ startingTop: '4%', // Starting top style attribute
+ endingTop: '10%', // Ending top style attribute
+ ready: function(modal, trigger) { // Callback for Modal open. Modal and trigger parameters available.
+ if ($(trigger).attr("href") == "#logEntryListModal") {
+ $(modal).find('#modal-entry-list').find("li").remove();
+ paraMap['path'] = trigger.text().trim();
+ var fileName = paraMap['path'].substring(paraMap['path'].lastIndexOf('/')+1); // Getting filename
+ $(modal).find('#entry-list-modal-title').text(fileName); // Set file name for modal window
+ var url = "${requestScope['javax.servlet.forward.servlet_path']}?path=" + paraMap['path'];
+ $(modal).find('#downloadLink').prop('href', url + "&action=download");
+ $.get( url, function(data) {
+ var entryList = $(modal).find('#modal-entry-list');
+ $(data.entryList).each(function( index, element ) {
+ entryList.append("<li><a href='#logEntryViewModal'>" + element + "</a></li>");
+ });
+ });
+ } else {
+ paraMap['entry'] = trigger.text().trim();
+ $(modal).find('#entry-view-modal-title').text(paraMap['entry']);
+ var entryUrl = "${requestScope['javax.servlet.forward.servlet_path']}?path=" + paraMap['path'] + "&entry=" + paraMap['entry'];
+ $.get( entryUrl, function(data) {
+ $(modal).find('#entry-view-modal-content').text(data.entryContent);
+ });
+ }
+ },
+ complete: function() {
+ console.log("modal closed!");
+ } // Callback for Modal close
+ });
});
</script>
@@ -55,7 +88,7 @@
<h3>File List</h3>
<c:forEach varStatus="fileLoop" var="fileName" items="${fileList}">
<p>
- <a href="${requestScope['javax.servlet.forward.servlet_path']}?path=${fileName}">
+ <a href="#logEntryListModal">
<c:out value="${fileName}"></c:out>
</a>
</p>
@@ -64,6 +97,40 @@
</c:forEach>
</div>
</div>
+
<%@ include file="footer.jsp" %>
+
+ <!-- Modal For Zip file entries -->
+ <div id="logEntryListModal" class="modal">
+ <div class="modal-content">
+ <h4 id="entry-list-modal-title" class="truncate"></h4>
+ <div id="entry-list-modal-content">
+ <ul id="modal-entry-list"></ul>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <div class="row">
+ <div class="col s3 offset-s6">
+ <a href="#!" id="downloadLink" class="modal-action modal-close waves-effect waves-green btn">Download</a>
+ </div>
+ <div class="col s3">
+ <a href="#!" class="modal-action modal-close waves-effect waves-green btn">Close</a>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Modal For Zip file entry's content -->
+ <div id="logEntryViewModal" class="modal modal-fixed-footer">
+ <div class="modal-content">
+ <h4 id="entry-view-modal-title" class="truncate"></h4>
+ <div id="entry-view-modal-content">
+
+ </div>
+ </div>
+ <div class="modal-footer">
+ <a href="#!" class="modal-action modal-close waves-effect waves-green btn">Close</a>
+ </div>
+ </div>
</body>
</html>