Created
August 25, 2020 19:13
-
-
Save jasonk000/a9b81205a40cf5358a712f3770da854e to your computer and use it in GitHub Desktop.
Eclipse JIFA customization hooks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/backend/build.gradle b/backend/build.gradle | |
index 6c5c21b..5486148 100644 | |
--- a/backend/build.gradle | |
+++ b/backend/build.gradle | |
@@ -14,7 +14,7 @@ subprojects { | |
apply plugin: 'java' | |
ext { | |
- vertx_version = '3.8.3' | |
+ vertx_version = '3.9.2' | |
} | |
dependencies { | |
diff --git a/backend/worker/src/main/java/org/eclipse/jifa/worker/Constant.java b/backend/worker/src/main/java/org/eclipse/jifa/worker/Constant.java | |
index 483e696..b5f8abf 100644 | |
--- a/backend/worker/src/main/java/org/eclipse/jifa/worker/Constant.java | |
+++ b/backend/worker/src/main/java/org/eclipse/jifa/worker/Constant.java | |
@@ -42,5 +42,7 @@ public interface Constant extends org.eclipse.jifa.common.Constant { | |
String API_PREFIX = "api.prefix"; | |
String SERVER_HOST_KEY = "server.host"; | |
String SERVER_PORT_KEY = "server.port"; | |
+ String SERVER_UPLOAD_DIR_KEY = "server.uploadDir"; | |
+ String HOOKS_NAME_KEY = "hooks.className"; | |
} | |
} | |
diff --git a/backend/worker/src/main/java/org/eclipse/jifa/worker/Global.java b/backend/worker/src/main/java/org/eclipse/jifa/worker/Global.java | |
index c7f8756..fecec92 100644 | |
--- a/backend/worker/src/main/java/org/eclipse/jifa/worker/Global.java | |
+++ b/backend/worker/src/main/java/org/eclipse/jifa/worker/Global.java | |
@@ -36,9 +36,11 @@ public class Global { | |
private static String WORKSPACE; | |
+ private static JifaHooks HOOKS; | |
+ | |
private static boolean initialized; | |
- static synchronized void init(Vertx vertx, String host, int port, JsonObject config) { | |
+ static synchronized void init(Vertx vertx, String host, int port, JsonObject config, JifaHooks hooks) { | |
if (initialized) { | |
return; | |
} | |
@@ -47,6 +49,7 @@ public class Global { | |
HOST = host; | |
PORT = port; | |
CONFIG = config; | |
+ HOOKS = hooks; | |
WORKSPACE = CONFIG.getString(Constant.ConfigKey.WORKSPACE, Constant.Misc.DEFAULT_WORKSPACE); | |
LOGGER.debug("Workspace: {}", WORKSPACE); | |
@@ -71,4 +74,8 @@ public class Global { | |
public static String workspace() { | |
return WORKSPACE; | |
} | |
+ | |
+ public static JifaHooks hooks() { | |
+ return HOOKS; | |
+ } | |
} | |
diff --git a/backend/worker/src/main/java/org/eclipse/jifa/worker/JifaHooks.java b/backend/worker/src/main/java/org/eclipse/jifa/worker/JifaHooks.java | |
new file mode 100644 | |
index 0000000..0318802 | |
--- /dev/null | |
+++ b/backend/worker/src/main/java/org/eclipse/jifa/worker/JifaHooks.java | |
@@ -0,0 +1,35 @@ | |
+package org.eclipse.jifa.worker; | |
+ | |
+import io.vertx.ext.web.Router; | |
+import io.vertx.core.http.HttpServerOptions; | |
+import io.vertx.core.json.JsonObject; | |
+import org.eclipse.jifa.common.enums.FileType; | |
+ | |
+public interface JifaHooks { | |
+ // set config | |
+ default void init(JsonObject config) {} | |
+ | |
+ // http server configuration | |
+ default HttpServerOptions serverOptions() { | |
+ return new HttpServerOptions(); | |
+ } | |
+ | |
+ // routes | |
+ default void beforeRoutes(Router router) {} | |
+ default void afterRoutes(Router router) {} | |
+ | |
+ // files | |
+ default String mapDirPath(FileType type, String name, String defaultFile) { | |
+ return defaultFile; | |
+ } | |
+ default String mapFilePath(FileType type, String name, String childrenName, String defaultFile) { | |
+ return defaultFile; | |
+ } | |
+ default String mapIndexPath(FileType fileType, String file, String defaultFile) { | |
+ return defaultFile; | |
+ } | |
+ | |
+ public class EmptyHooks implements JifaHooks { | |
+ // use default implementations | |
+ } | |
+} | |
diff --git a/backend/worker/src/main/java/org/eclipse/jifa/worker/Starter.java b/backend/worker/src/main/java/org/eclipse/jifa/worker/Starter.java | |
index fe07a83..6ff7a3b 100644 | |
--- a/backend/worker/src/main/java/org/eclipse/jifa/worker/Starter.java | |
+++ b/backend/worker/src/main/java/org/eclipse/jifa/worker/Starter.java | |
@@ -18,6 +18,7 @@ import io.vertx.core.DeploymentOptions; | |
import io.vertx.core.Vertx; | |
import io.vertx.core.VertxOptions; | |
import io.vertx.core.http.HttpServer; | |
+import io.vertx.core.http.HttpServerOptions; | |
import io.vertx.core.json.JsonObject; | |
import io.vertx.ext.web.Router; | |
import io.vertx.ext.web.handler.BodyHandler; | |
@@ -31,6 +32,7 @@ import org.slf4j.LoggerFactory; | |
import java.io.File; | |
import java.io.IOException; | |
+import java.lang.ReflectiveOperationException; | |
import java.net.ServerSocket; | |
import java.nio.charset.Charset; | |
import java.util.Objects; | |
@@ -38,6 +40,8 @@ import java.util.concurrent.CountDownLatch; | |
import static org.eclipse.jifa.worker.Constant.ConfigKey.SERVER_HOST_KEY; | |
import static org.eclipse.jifa.worker.Constant.ConfigKey.SERVER_PORT_KEY; | |
+import static org.eclipse.jifa.worker.Constant.ConfigKey.SERVER_UPLOAD_DIR_KEY; | |
+import static org.eclipse.jifa.worker.Constant.ConfigKey.HOOKS_NAME_KEY; | |
import static org.eclipse.jifa.worker.Constant.Misc.*; | |
public class Starter extends AbstractVerticle { | |
@@ -81,6 +85,24 @@ public class Starter extends AbstractVerticle { | |
} | |
} | |
+ JifaHooks findHooks() { | |
+ JifaHooks hook = null; | |
+ | |
+ if (config().containsKey(HOOKS_NAME_KEY)) { | |
+ String className = config().getString(HOOKS_NAME_KEY); | |
+ try { | |
+ LOGGER.info("applying hooks: " + className); | |
+ Class<JifaHooks> clazz = (Class<JifaHooks>) Class.forName(className); | |
+ hook = clazz.getConstructor().newInstance(); | |
+ hook.init(config()); | |
+ } catch (ReflectiveOperationException e) { | |
+ LOGGER.warn("could not start hook class: " + className + ", due to error", e); | |
+ } | |
+ } | |
+ | |
+ return hook != null ? hook : new JifaHooks.EmptyHooks(); | |
+ } | |
+ | |
@Override | |
public void start() { | |
String host = config().containsKey(SERVER_HOST_KEY) ? config().getString(SERVER_HOST_KEY) : DEFAULT_HOST; | |
@@ -89,12 +111,25 @@ public class Starter extends AbstractVerticle { | |
String staticRoot = System.getProperty(WEB_ROOT_KEY, "webroot"); | |
+ String uploadDir = config().containsKey(SERVER_UPLOAD_DIR_KEY) ? config().getString(SERVER_UPLOAD_DIR_KEY) : null; | |
+ | |
+ JifaHooks hooks = findHooks(); | |
+ | |
vertx.executeBlocking(event -> { | |
- Global.init(vertx, host, port, config()); | |
+ Global.init(vertx, host, port, config(), hooks); | |
- HttpServer server = vertx.createHttpServer(); | |
+ HttpServer server = vertx.createHttpServer(hooks.serverOptions()); | |
Router router = Router.router(vertx); | |
+ // body handler always needs to be first so it can read the body | |
+ if (uploadDir == null) { | |
+ router.post().handler(BodyHandler.create()); | |
+ } else { | |
+ router.post().handler(BodyHandler.create(uploadDir)); | |
+ } | |
+ | |
+ hooks.beforeRoutes(router); | |
+ | |
File webRoot = new File(staticRoot); | |
if (webRoot.exists() && webRoot.isDirectory()) { | |
String staticPattern = "^(?!" + Global.stringConfig(Constant.ConfigKey.API_PREFIX) + ").*$"; | |
@@ -105,11 +140,10 @@ public class Starter extends AbstractVerticle { | |
} | |
// cors | |
router.route().handler(CorsHandler.create("*")); | |
- router.post().handler(BodyHandler.create()); | |
new RouteFiller(router).fill(); | |
+ hooks.afterRoutes(router); | |
server.requestHandler(router); | |
- | |
server.listen(port, host, ar -> { | |
if (ar.succeeded()) { | |
event.complete(); | |
@@ -126,5 +160,4 @@ public class Starter extends AbstractVerticle { | |
} | |
}); | |
} | |
- | |
} | |
diff --git a/backend/worker/src/main/java/org/eclipse/jifa/worker/route/AnalysisRoute.java b/backend/worker/src/main/java/org/eclipse/jifa/worker/route/AnalysisRoute.java | |
index 4fefaf2..c484c8a 100644 | |
--- a/backend/worker/src/main/java/org/eclipse/jifa/worker/route/AnalysisRoute.java | |
+++ b/backend/worker/src/main/java/org/eclipse/jifa/worker/route/AnalysisRoute.java | |
@@ -23,15 +23,12 @@ import org.eclipse.jifa.worker.support.FileSupport; | |
import io.vertx.core.Future; | |
import io.vertx.core.http.HttpMethod; | |
import io.vertx.core.http.HttpServerRequest; | |
-import org.slf4j.Logger; | |
-import org.slf4j.LoggerFactory; | |
import java.io.File; | |
import java.util.Map; | |
@MappingPrefix(value = {"/heap-dump/:file"}) | |
class AnalysisRoute extends BaseRoute { | |
- private static final Logger LOGGER = LoggerFactory.getLogger(AnalysisRoute.class); | |
private Analyzer helper = Analyzer.getInstance(); | |
// TODO: not good enough | |
diff --git a/backend/worker/src/main/java/org/eclipse/jifa/worker/support/FileSupport.java b/backend/worker/src/main/java/org/eclipse/jifa/worker/support/FileSupport.java | |
index bdc9727..58328aa 100644 | |
--- a/backend/worker/src/main/java/org/eclipse/jifa/worker/support/FileSupport.java | |
+++ b/backend/worker/src/main/java/org/eclipse/jifa/worker/support/FileSupport.java | |
@@ -214,7 +214,8 @@ public class FileSupport { | |
} | |
public static String dirPath(FileType type, String name) { | |
- return dirPath(type) + File.separator + name; | |
+ String defaultF = dirPath(type) + File.separator + name; | |
+ return Global.hooks().mapDirPath(type, name, defaultF); | |
} | |
private static String infoFilePath(FileType type, String name) { | |
@@ -230,7 +231,8 @@ public class FileSupport { | |
} | |
private static String filePath(FileType type, String name, String childrenName) { | |
- return dirPath(type, name) + File.separator + childrenName; | |
+ String defaultF = dirPath(type, name) + File.separator + childrenName; | |
+ return Global.hooks().mapFilePath(type, name, childrenName, defaultF); | |
} | |
public static String errorLogPath(FileType fileType, String file) { | |
@@ -245,7 +247,8 @@ public class FileSupport { | |
} else { | |
indexFileNamePrefix = file + '.'; | |
} | |
- return FileSupport.filePath(fileType, file, indexFileNamePrefix + "index"); | |
+ String defaultFile = FileSupport.filePath(fileType, file, indexFileNamePrefix + "index"); | |
+ return Global.hooks().mapIndexPath(fileType, file, defaultFile); | |
} | |
public static TransferListener createTransferListener(FileType fileType, String originalName, String fileName) { | |
diff --git a/frontend/src/Jifa.js b/frontend/src/Jifa.js | |
index 634a3b9..10331de 100644 | |
--- a/frontend/src/Jifa.js | |
+++ b/frontend/src/Jifa.js | |
@@ -18,4 +18,9 @@ export default class JifaGlobal { | |
static dev() { | |
return process.env.NODE_ENV === 'development' | |
} | |
+ | |
+ static fileManagement() { | |
+ // default enabled, but can be disabled by setting 'false' | |
+ return process.env.VUE_APP_JIFA_FILE_MGMT !== "false" | |
+ } | |
} | |
diff --git a/frontend/src/components/menu/AnalysisResultMenu.vue b/frontend/src/components/menu/AnalysisResultMenu.vue | |
index 14fe8c8..eb49f3a 100644 | |
--- a/frontend/src/components/menu/AnalysisResultMenu.vue | |
+++ b/frontend/src/components/menu/AnalysisResultMenu.vue | |
@@ -12,11 +12,11 @@ | |
--> | |
<template> | |
<b-navbar-nav> | |
- <b-nav-item href="#" @click="doReanalyze" v-if="analysisState === 'SUCCESS' || analysisState === 'ERROR'"> | |
+ <b-nav-item href="#" @click="doReanalyze" v-if="$jifa.fileManagement() && analysisState === 'SUCCESS' || analysisState === 'ERROR'"> | |
<i class="el-icon-warning-outline" style="margin-right: 3px"/> {{$t("jifa.reanalyze")}} | |
</b-nav-item> | |
- <b-nav-item href="#" @click="doRelease" v-if="analysisState === 'SUCCESS'"> | |
+ <b-nav-item href="#" @click="doRelease" v-if="$jifa.fileManagement() && analysisState === 'SUCCESS'"> | |
<i class="el-icon-s-release" style="margin-right: 3px"/> {{$t("jifa.release")}} | |
</b-nav-item> | |
@@ -49,27 +49,31 @@ | |
methods: { | |
doReanalyze() { | |
- this.$confirm(this.$t('jifa.heap.reanalyzePrompt'), this.$t('jifa.prompt'), { | |
- confirmButtonText: this.$t('jifa.confirm'), | |
- cancelButtonText: this.$t('jifa.cancel'), | |
- type: 'warning' | |
- }).then(() => { | |
- axios.post(this.getUrlByType('clean')).then(() => { | |
- window.location.reload(); | |
+ if (this.$jifa.fileManagement()) { | |
+ this.$confirm(this.$t('jifa.heap.reanalyzePrompt'), this.$t('jifa.prompt'), { | |
+ confirmButtonText: this.$t('jifa.confirm'), | |
+ cancelButtonText: this.$t('jifa.cancel'), | |
+ type: 'warning' | |
+ }).then(() => { | |
+ axios.post(this.getUrlByType('clean')).then(() => { | |
+ window.location.reload(); | |
+ }) | |
}) | |
- }) | |
+ } | |
}, | |
doRelease() { | |
- this.$confirm(this.$t('jifa.heap.releasePrompt'), this.$t('jifa.prompt'), { | |
- confirmButtonText: this.$t('jifa.confirm'), | |
- cancelButtonText: this.$t('jifa.cancel'), | |
- type: 'warning' | |
- }).then(() => { | |
- axios.post(this.getUrlByType('release')).then(() => { | |
- this.$router.push({name: 'finder'}) | |
+ if (this.$jifa.fileManagement()) { | |
+ this.$confirm(this.$t('jifa.heap.releasePrompt'), this.$t('jifa.prompt'), { | |
+ confirmButtonText: this.$t('jifa.confirm'), | |
+ cancelButtonText: this.$t('jifa.cancel'), | |
+ type: 'warning' | |
+ }).then(() => { | |
+ axios.post(this.getUrlByType('release')).then(() => { | |
+ this.$router.push({name: 'finder'}) | |
+ }) | |
}) | |
- }) | |
+ } | |
}, | |
triggerInspector() { | |
diff --git a/frontend/src/components/menu/FinderMenu.vue b/frontend/src/components/menu/FinderMenu.vue | |
index ef4c3f2..59527a0 100644 | |
--- a/frontend/src/components/menu/FinderMenu.vue | |
+++ b/frontend/src/components/menu/FinderMenu.vue | |
@@ -13,12 +13,12 @@ | |
<template> | |
<b-navbar-nav> | |
- <b-nav-item @click="$emit('chooseMenu', 'HEAP_DUMP')" | |
+ <b-nav-item @click="$emit('chooseMenu', 'HEAP_DUMP')" v-if="$jifa.fileManagement()" | |
:active="fileType==='HEAP_DUMP'"> | |
<i class="el-icon-coin" style="margin-right: 3px"/> {{$t("jifa.heapDumpAnalysis")}} | |
</b-nav-item> | |
- <b-nav-item @click="handleAddFile"> | |
+ <b-nav-item @click="handleAddFile" v-if="$jifa.fileManagement()"> | |
<i class="el-icon-plus" style="margin-right: 3px"/> {{this.title}} | |
</b-nav-item> | |
</b-navbar-nav> | |
diff --git a/frontend/src/components/menu/ViewMenu.vue b/frontend/src/components/menu/ViewMenu.vue | |
index 61977e7..eded36d 100644 | |
--- a/frontend/src/components/menu/ViewMenu.vue | |
+++ b/frontend/src/components/menu/ViewMenu.vue | |
@@ -13,7 +13,11 @@ | |
<template> | |
<b-navbar type="light" variant="faded" style="height: 100%; border-bottom: 1px solid #dcdfe6; font-size: 14px"> | |
<b-navbar-nav> | |
- <b-navbar-brand href="" to="/"> | |
+ <b-navbar-brand href="" to="/" v-if="$jifa.fileManagement()"> | |
+ <i class="el-icon-s-platform"/> | |
+ J I F A | |
+ </b-navbar-brand> | |
+ <b-navbar-brand v-if="!$jifa.fileManagement()"> | |
<i class="el-icon-s-platform"/> | |
J I F A | |
</b-navbar-brand> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/backend/worker/src/main/java/com/netflix/cldperf/fc/NetflixHooks.java b/backend/worker/src/main/java/com/netflix/cldperf/fc/NetflixHooks.java | |
new file mode 100644 | |
index 0000000..52b7d03 | |
--- /dev/null | |
+++ b/backend/worker/src/main/java/com/netflix/cldperf/fc/NetflixHooks.java | |
@@ -0,0 +1,103 @@ | |
+package com.netflix.cldperf.fc; | |
+ | |
+import com.netflix.gandalf.agent.AuthorizationClient; | |
+ | |
+import io.vertx.core.http.HttpMethod; | |
+import io.vertx.core.http.HttpServerRequest; | |
+import io.vertx.core.http.HttpServerOptions; | |
+import io.vertx.core.json.JsonObject; | |
+import io.vertx.ext.web.Router; | |
+ | |
+import org.slf4j.Logger; | |
+import org.slf4j.LoggerFactory; | |
+ | |
+import org.eclipse.jifa.common.enums.FileType; | |
+import org.eclipse.jifa.worker.JifaHooks; | |
+import org.eclipse.jifa.worker.support.FileSupport; | |
+ | |
+public class NetflixHooks implements JifaHooks { | |
+ private static final Logger LOGGER = LoggerFactory.getLogger(NetflixHooks.class); | |
+ | |
+ String SOMEWHERE_ELSE = null; | |
+ String AUTH_API = null; | |
+ AuthorizationClient authorizationClient = null; | |
+ | |
+ @Override | |
+ public void init(JsonObject config) { | |
+ // environment specific so it is simpler to use the netflix environment.d support | |
+ SOMEWHERE_ELSE = System.getenv("SOMEWHERE_ELSE"); | |
+ AUTH_API = System.getenv("AUTH_API"); | |
+ | |
+ // connect and check gandalf is available | |
+ this.authorizationClient = new AuthorizationClient(); | |
+ this.authorizationClient.status(); | |
+ } | |
+ | |
+ @Override | |
+ public HttpServerOptions serverOptions() { | |
+ // our OIDC headers can be huge | |
+ return new HttpServerOptions() | |
+ .setMaxHeaderSize(64*1024); | |
+ } | |
+ | |
+ @Override | |
+ public void beforeRoutes(Router router) { | |
+ // /heapDump spa should be loaded as if it was /index.html | |
+ // in other words serve index.html when /heapDump is requested | |
+ router.get("/heapDump*").handler(routingContext -> { | |
+ routingContext.reroute("/index.html"); | |
+ }); | |
+ | |
+ // health check | |
+ router.get("/healthcheck").handler(routingContext -> { | |
+ routingContext.response().end("OK"); | |
+ }); | |
+ | |
+ // redirect to flamecommander | |
+ router.get("/").handler(routingContext -> { | |
+ routingContext.response() | |
+ .setStatusCode(303) | |
+ .putHeader("Location", SOMEWHERE_ELSE) | |
+ .end(); | |
+ }); | |
+ | |
+ // check the user is authorized | |
+ router.route("/jifa-api/heap-dump/:file/*").blockingHandler( | |
+ new NetflixAuthHandler(authorizationClient, AUTH_API)); | |
+ } | |
+ | |
+ @Override | |
+ public void afterRoutes(Router router) { | |
+ router.route().failureHandler(frc -> { | |
+ LOGGER.error("unhandled error: ", frc.failure()); | |
+ frc.response().setStatusCode(500).end("Internal Server Error"); | |
+ }); | |
+ } | |
+ | |
+ @Override | |
+ public String mapDirPath(FileType type, String name, String defaultFile) { | |
+ if (name.startsWith("s3!")) { | |
+ String[] parts = name.split("!"); | |
+ if (parts.length == 2) { | |
+ return "/offline-heapdumps/" + parts[1]; | |
+ } | |
+ } | |
+ return defaultFile; | |
+ } | |
+ | |
+ @Override | |
+ public String mapFilePath(FileType type, String name, String childrenName, String defaultFile) { | |
+ if (name.startsWith("s3!") && name.equals(childrenName)) { | |
+ return mapDirPath(type, name, name) + "/heapdump.hprof"; | |
+ } | |
+ return defaultFile; | |
+ } | |
+ | |
+ @Override | |
+ public String mapIndexPath(FileType fileType, String file, String defaultFile) { | |
+ if (file.startsWith("s3!")) { | |
+ return mapDirPath(fileType, file, file) + "/heapdump.index"; | |
+ } | |
+ return defaultFile; | |
+ } | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment