Skip to content

Instantly share code, notes, and snippets.

@jasonk000
Created August 25, 2020 19:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jasonk000/a9b81205a40cf5358a712f3770da854e to your computer and use it in GitHub Desktop.
Save jasonk000/a9b81205a40cf5358a712f3770da854e to your computer and use it in GitHub Desktop.
Eclipse JIFA customization hooks
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>
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