azkaban-aplcache
Details
azkaban-common/build.gradle 1(+1 -0)
diff --git a/azkaban-common/build.gradle b/azkaban-common/build.gradle
index 5f69d20..967fe24 100644
--- a/azkaban-common/build.gradle
+++ b/azkaban-common/build.gradle
@@ -41,6 +41,7 @@ dependencies {
compile deps.dbutils
compile deps.fileupload
compile deps.guava
+ compile deps.gson
compile deps.hadoopAnnotations
compile deps.hadoopAuth
compile deps.hadoopCommon
azkaban-exec-server/build.gradle 1(+0 -1)
diff --git a/azkaban-exec-server/build.gradle b/azkaban-exec-server/build.gradle
index 353df7f..0436e3d 100644
--- a/azkaban-exec-server/build.gradle
+++ b/azkaban-exec-server/build.gradle
@@ -4,7 +4,6 @@ dependencies {
compile(project(':azkaban-common'))
compile deps.kafkaLog4jAppender
- compile(deps.gson)
runtime(project(':azkaban-hadoop-security-plugin'))
diff --git a/azkaban-web-server/src/main/java/azkaban/webapp/AzkabanWebServer.java b/azkaban-web-server/src/main/java/azkaban/webapp/AzkabanWebServer.java
index dbc8c9b..8aa7acc 100644
--- a/azkaban-web-server/src/main/java/azkaban/webapp/AzkabanWebServer.java
+++ b/azkaban-web-server/src/main/java/azkaban/webapp/AzkabanWebServer.java
@@ -58,6 +58,7 @@ import azkaban.webapp.servlet.ProjectManagerServlet;
import azkaban.webapp.servlet.ProjectServlet;
import azkaban.webapp.servlet.ScheduleServlet;
import azkaban.webapp.servlet.StatsServlet;
+import azkaban.webapp.servlet.StatusServlet;
import azkaban.webapp.servlet.TriggerManagerServlet;
import com.google.inject.Guice;
import com.google.inject.Inject;
@@ -125,8 +126,9 @@ public class AzkabanWebServer extends AzkabanServer {
@Deprecated
private static AzkabanWebServer app;
+
private final VelocityEngine velocityEngine;
-
+ private final StatusService statusService;
private final Server server;
private final UserManager userManager;
private final ProjectManager projectManager;
@@ -151,7 +153,8 @@ public class AzkabanWebServer extends AzkabanServer {
final SessionCache sessionCache,
final UserManager userManager,
final ScheduleManager scheduleManager,
- final VelocityEngine velocityEngine) {
+ final VelocityEngine velocityEngine,
+ final StatusService statusService) {
this.props = requireNonNull(props, "props is null.");
this.server = requireNonNull(server, "server is null.");
this.executorManager = requireNonNull(executorManager, "executorManager is null.");
@@ -162,6 +165,7 @@ public class AzkabanWebServer extends AzkabanServer {
this.userManager = requireNonNull(userManager, "userManager is null.");
this.scheduleManager = requireNonNull(scheduleManager, "scheduleManager is null.");
this.velocityEngine = requireNonNull(velocityEngine, "velocityEngine is null.");
+ this.statusService = statusService;
loadBuiltinCheckersAndActions();
@@ -471,6 +475,7 @@ public class AzkabanWebServer extends AzkabanServer {
root.addServlet(new ServletHolder(new JMXHttpServlet()), "/jmx");
root.addServlet(new ServletHolder(new TriggerManagerServlet()), "/triggers");
root.addServlet(new ServletHolder(new StatsServlet()), "/stats");
+ root.addServlet(new ServletHolder(new StatusServlet(this.statusService)), "/status");
final ServletHolder restliHolder = new ServletHolder(new RestliServlet());
restliHolder.setInitParameter("resourcePackages", "azkaban.restli");
diff --git a/azkaban-web-server/src/main/java/azkaban/webapp/servlet/StatusServlet.java b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/StatusServlet.java
new file mode 100644
index 0000000..3514eec
--- /dev/null
+++ b/azkaban-web-server/src/main/java/azkaban/webapp/servlet/StatusServlet.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 LinkedIn Corp.
+ *
+ * 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.
+ *
+ */
+
+package azkaban.webapp.servlet;
+
+import static azkaban.webapp.servlet.AbstractAzkabanServlet.JSON_MIME_TYPE;
+
+import azkaban.webapp.StatusService;
+import com.google.gson.GsonBuilder;
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StatusServlet extends HttpServlet {
+
+ private static final Logger log = LoggerFactory.getLogger(StatusServlet.class);
+
+ private final StatusService statusService;
+
+ public StatusServlet(final StatusService statusService) {
+ this.statusService = statusService;
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
+ throws ServletException, IOException {
+ try {
+ resp.setContentType(JSON_MIME_TYPE);
+ resp.getOutputStream()
+ .println(new GsonBuilder()
+ .setPrettyPrinting()
+ .create()
+ .toJson(this.statusService.getStatus()));
+ resp.setStatus(HttpServletResponse.SC_OK);
+ } catch (final Exception e) {
+ log.error("Error!! while reporting status: ", e);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
+ } finally {
+ resp.getOutputStream().close();
+ }
+ }
+}
diff --git a/azkaban-web-server/src/main/java/azkaban/webapp/Status.java b/azkaban-web-server/src/main/java/azkaban/webapp/Status.java
new file mode 100644
index 0000000..c2ffeed
--- /dev/null
+++ b/azkaban-web-server/src/main/java/azkaban/webapp/Status.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 LinkedIn Corp.
+ *
+ * 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.
+ *
+ */
+
+package azkaban.webapp;
+
+import azkaban.executor.Executor;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+/**
+ * This POJO is used by GSON library to create a status JSON object. Certain warnings do not apply
+ * here.
+ */
+@SuppressWarnings({"FieldCanBeLocal", "unused"})
+public class Status {
+
+ private final String version;
+ private final String installationPath;
+ private final long usedMemory, xmx;
+ private final boolean isDatabaseUp;
+ private final Map<Integer, Executor> executorStatusMap;
+
+ Status(final String version,
+ final String installationPath,
+ final long usedMemory,
+ final long xmx,
+ final boolean isDatabaseUp,
+ final Map<Integer, Executor> executorStatusMap) {
+ this.version = version;
+ this.installationPath = installationPath;
+ this.usedMemory = usedMemory;
+ this.xmx = xmx;
+ this.isDatabaseUp = isDatabaseUp;
+ this.executorStatusMap = ImmutableMap.copyOf(executorStatusMap);
+ }
+}
diff --git a/azkaban-web-server/src/main/java/azkaban/webapp/StatusService.java b/azkaban-web-server/src/main/java/azkaban/webapp/StatusService.java
new file mode 100644
index 0000000..8472fb9
--- /dev/null
+++ b/azkaban-web-server/src/main/java/azkaban/webapp/StatusService.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2017 LinkedIn Corp.
+ *
+ * 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.
+ *
+ */
+
+package azkaban.webapp;
+
+import static azkaban.webapp.servlet.AbstractAzkabanServlet.jarVersion;
+
+import azkaban.db.DatabaseOperator;
+import azkaban.executor.Executor;
+import azkaban.executor.ExecutorLoader;
+import azkaban.executor.ExecutorManagerException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.io.File;
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class StatusService {
+
+ private static final Logger log = LoggerFactory.getLogger(StatusService.class);
+ private static final File PACKAGE_JAR = new File(
+ StatusService.class.getProtectionDomain().getCodeSource().getLocation().getPath());
+
+ private final ExecutorLoader executorLoader;
+ private final DatabaseOperator dbOperator;
+
+ @Inject
+ public StatusService(final ExecutorLoader executorLoader, final DatabaseOperator dbOperator) {
+ this.executorLoader = executorLoader;
+ this.dbOperator = dbOperator;
+ }
+
+ private static String getInstallationPath() {
+ try {
+ return PACKAGE_JAR.getCanonicalPath();
+ } catch (final IOException e) {
+ log.error("Unable to obtain canonical path. Reporting absolute path instead", e);
+ return PACKAGE_JAR.getAbsolutePath();
+ }
+ }
+
+ public Status getStatus() {
+ final String version = jarVersion == null ? "unknown" : jarVersion;
+ final Runtime runtime = Runtime.getRuntime();
+ final long usedMemory = runtime.totalMemory() - runtime.freeMemory();
+
+ // Build the status object
+ return new Status(version,
+ getInstallationPath(),
+ usedMemory,
+ runtime.maxMemory(),
+ getDbStatus(),
+ getActiveExecutors());
+ }
+
+ private Map<Integer, Executor> getActiveExecutors() {
+ final Map<Integer, Executor> executorMap = new HashMap<>();
+ try {
+ final List<Executor> executors = this.executorLoader.fetchActiveExecutors();
+ for (final Executor executor : executors) {
+ executorMap.put(executor.getId(), executor);
+ }
+ } catch (final ExecutorManagerException e) {
+ log.error("Fetching executors failed!", e);
+ }
+ return executorMap;
+ }
+
+ private boolean getDbStatus() {
+ try {
+ return this.dbOperator.query("SELECT 1", rs -> true);
+ } catch (final SQLException e) {
+ log.error("DB Error", e);
+ }
+ return false;
+ }
+
+}