Repository: zeppelin
Updated Branches:
  refs/heads/master 1ac0e2f00 -> 0f9a1a80c


[ZEPPELIN-3600] Add REST API to change logger level dynamically

### What is this PR for?
Enabling users to change the level of a specific logger to investigate Zeppelin 
server's status.

### What type of PR is it?
[Feature]

### Todos
* [x] - Add api to change the level of logger
* [x] - Add exception handler

### What is the Jira issue?
* https://issues.apache.org/jira/browse/ZEPPELIN-3600

### How should this be tested?
1. http --session ~/session.txt --form POST http://localhost:8080/api/login 
userName=admin password=password1
1. http --session ~/session.txt POST http://localhost:8080/api/admin 
name=org.apache.zeppelin.rest.AdminRestApi level=DEBUG

### Screenshots (if appropriate)

### Questions:
* Does the licenses files need update? No
* Is there breaking changes for older versions? No
* Does this needs documentation? May be

Author: Jongyoul Lee <jongy...@gmail.com>

Closes #3060 from jongyoul/ZEPPELIN-3600 and squashes the following commits:

618f76074 [Jongyoul Lee] Add admin permisson to aceess /api/admin/** Change 
error message clearly to show a specific problem
04308bd75 [Jongyoul Lee] Change `Preconditions` to a self-check logic to throw 
BadRequestException Add `WebApplicationExceptionMapper` to the server Implement 
`ExceptionSerializer` for gson Implement `WebApplicationExceptionMapper`
5127d89b4 [Jongyoul Lee] Add api to show all loggers' name and level Add api to 
show a specific logger's name and level Add api to change a logger's level


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/0f9a1a80
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/0f9a1a80
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/0f9a1a80

Branch: refs/heads/master
Commit: 0f9a1a80c76054c322e2d1768cf24a3e4bd49496
Parents: 1ac0e2f
Author: Jongyoul Lee <jongy...@gmail.com>
Authored: Wed Jul 11 16:16:43 2018 +0900
Committer: Jongyoul Lee <jongy...@apache.org>
Committed: Thu Jul 12 13:43:33 2018 +0900

----------------------------------------------------------------------
 conf/shiro.ini.template                         |  1 +
 .../org/apache/zeppelin/rest/AdminRestApi.java  | 82 +++++++++++++++++
 .../WebApplicationExceptionMapper.java          | 44 +++++++++
 .../zeppelin/rest/message/LoggerRequest.java    | 41 +++++++++
 .../rest/message/gson/ExceptionSerializer.java  | 42 +++++++++
 .../rest/message/gson/LoggerSerializer.java     | 52 +++++++++++
 .../apache/zeppelin/server/GsonProvider.java    | 93 ++++++++++++++++++++
 .../apache/zeppelin/server/ZeppelinServer.java  | 13 +++
 .../apache/zeppelin/service/AdminService.java   | 78 ++++++++++++++++
 .../zeppelin/service/AdminServiceTest.java      | 48 ++++++++++
 10 files changed, 494 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/0f9a1a80/conf/shiro.ini.template
----------------------------------------------------------------------
diff --git a/conf/shiro.ini.template b/conf/shiro.ini.template
index 9397025..89e4785 100644
--- a/conf/shiro.ini.template
+++ b/conf/shiro.ini.template
@@ -116,5 +116,6 @@ admin = *
 /api/interpreter/** = authc, roles[admin]
 /api/configurations/** = authc, roles[admin]
 /api/credential/** = authc, roles[admin]
+/api/admin/** = authc, roles[admin]
 #/** = anon
 /** = authc

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/0f9a1a80/zeppelin-server/src/main/java/org/apache/zeppelin/rest/AdminRestApi.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/AdminRestApi.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/AdminRestApi.java
new file mode 100644
index 0000000..093b710
--- /dev/null
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/AdminRestApi.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.zeppelin.rest;
+
+import com.google.common.collect.Lists;
+import java.util.List;
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import org.apache.commons.lang.StringUtils;
+import org.apache.zeppelin.annotation.ZeppelinApi;
+import org.apache.zeppelin.rest.message.LoggerRequest;
+import org.apache.zeppelin.service.AdminService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** This rest apis support some of feature related admin. e.g. changin log 
level. */
+@Path("/admin")
+public class AdminRestApi {
+  private static final Logger logger = 
LoggerFactory.getLogger(AdminRestApi.class);
+
+  private AdminService adminService;
+
+  public AdminRestApi(AdminService adminService) {
+    this.adminService = adminService;
+  }
+
+  /**
+   * It gets current loggers' name and level.
+   *
+   * @param name FQCN
+   * @return List of current loggers' name and level with json format. It 
returns all of loggers'
+   *     name and level without name. With name, it returns only specific 
logger's name and level.
+   */
+  @GET
+  @ZeppelinApi
+  public List<org.apache.log4j.Logger> getLoggerSetting(@QueryParam("name") 
String name) {
+    logger.debug("name: {}", name);
+    return null == name || name.isEmpty()
+        ? adminService.getLoggers()
+        : Lists.newArrayList(adminService.getLogger(name));
+  }
+
+  /**
+   * It change logger's level.
+   *
+   * @param loggerRequest logger's name and level with json format
+   * @return The changed logger's name and level.
+   */
+  @POST
+  @ZeppelinApi
+  public List<org.apache.log4j.Logger> setLoggerLevel(LoggerRequest 
loggerRequest) {
+    if (null == loggerRequest
+        || StringUtils.isEmpty(loggerRequest.getName())
+        || StringUtils.isEmpty(loggerRequest.getLevel())) {
+      logger.trace("loggerRequest: {}", loggerRequest);
+      throw new BadRequestException("Wrong request body");
+    }
+    logger.debug("loggerRequest: {}", loggerRequest);
+
+    adminService.setLoggerLevel(loggerRequest);
+
+    return Lists.newArrayList(adminService.getLogger(loggerRequest.getName()));
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/0f9a1a80/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/WebApplicationExceptionMapper.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/WebApplicationExceptionMapper.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/WebApplicationExceptionMapper.java
new file mode 100644
index 0000000..79e3fe0
--- /dev/null
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/WebApplicationExceptionMapper.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.zeppelin.rest.exception;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import org.apache.zeppelin.rest.message.gson.ExceptionSerializer;
+
+@Provider
+public class WebApplicationExceptionMapper implements 
ExceptionMapper<WebApplicationException> {
+  private final Gson gson;
+
+  public WebApplicationExceptionMapper() {
+    GsonBuilder gsonBuilder = new 
GsonBuilder().enableComplexMapKeySerialization();
+    gsonBuilder.registerTypeHierarchyAdapter(
+        Exception.class, new ExceptionSerializer());
+    this.gson = gsonBuilder.create();
+  }
+
+  @Override
+  public Response toResponse(WebApplicationException exception) {
+    return Response.status(exception.getResponse().getStatus())
+        .entity(gson.toJson(exception)).build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/0f9a1a80/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/LoggerRequest.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/LoggerRequest.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/LoggerRequest.java
new file mode 100644
index 0000000..e0c80bb
--- /dev/null
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/LoggerRequest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.zeppelin.rest.message;
+
+public class LoggerRequest {
+  private String name;
+  private String level;
+
+  public LoggerRequest(String name, String level) {
+    this.name = name;
+    this.level = level;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getLevel() {
+    return level;
+  }
+
+  @Override
+  public String toString() {
+    return "[name: " + name + ", level: " + level + "]";
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/0f9a1a80/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/gson/ExceptionSerializer.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/gson/ExceptionSerializer.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/gson/ExceptionSerializer.java
new file mode 100644
index 0000000..ac654c0
--- /dev/null
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/gson/ExceptionSerializer.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.zeppelin.rest.message.gson;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import java.lang.reflect.Type;
+import javax.ws.rs.WebApplicationException;
+
+public class ExceptionSerializer implements JsonSerializer<Exception> {
+
+  @Override
+  public JsonElement serialize(
+      Exception e, Type type, JsonSerializationContext 
jsonSerializationContext) {
+    JsonObject jsonObject = new JsonObject();
+    jsonObject.addProperty("exception", e.getClass().getSimpleName());
+    jsonObject.addProperty("message", e.getMessage());
+
+    if (e instanceof WebApplicationException) {
+      jsonObject.addProperty("status", ((WebApplicationException) 
e).getResponse().getStatus());
+    }
+
+    return jsonObject;
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/0f9a1a80/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/gson/LoggerSerializer.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/gson/LoggerSerializer.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/gson/LoggerSerializer.java
new file mode 100644
index 0000000..1d3aa47
--- /dev/null
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/gson/LoggerSerializer.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.zeppelin.rest.message.gson;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import java.lang.reflect.Type;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+public class LoggerSerializer implements JsonSerializer<Logger> {
+
+  @Override
+  public JsonElement serialize(
+      Logger logger, Type type, JsonSerializationContext 
jsonSerializationContext) {
+    JsonObject jsonObject = new JsonObject();
+    jsonObject.addProperty("name", logger.getName());
+    jsonObject.addProperty("level", getLoggerLevel(logger));
+    return jsonObject;
+  }
+
+  private String getLoggerLevel(Category logger) {
+    if (null == logger) {
+      return StringUtils.EMPTY;
+    }
+    Level level = logger.getLevel();
+    if (null != level) {
+      return level.toString();
+    } else {
+      return getLoggerLevel(logger.getParent());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/0f9a1a80/zeppelin-server/src/main/java/org/apache/zeppelin/server/GsonProvider.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/server/GsonProvider.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/server/GsonProvider.java
new file mode 100644
index 0000000..cc14e33
--- /dev/null
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/GsonProvider.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.zeppelin.server;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import org.apache.log4j.Logger;
+import org.apache.zeppelin.rest.message.LoggerRequest;
+import org.apache.zeppelin.rest.message.gson.LoggerSerializer;
+
+@Provider
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class GsonProvider<T> implements MessageBodyReader<T>, 
MessageBodyWriter<T> {
+  private final Gson gson;
+
+  public GsonProvider() {
+    GsonBuilder gsonBuilder = new 
GsonBuilder().enableComplexMapKeySerialization();
+    gsonBuilder.registerTypeAdapter(Logger.class, new LoggerSerializer());
+    this.gson = gsonBuilder.create();
+  }
+
+  @Override
+  public boolean isReadable(
+      Class<?> type, Type genericType, Annotation[] annotations, MediaType 
mediaType) {
+    return type == LoggerRequest.class; // For backward compatibility
+  }
+
+  @Override
+  public T readFrom(
+      Class<T> type,
+      Type genericType,
+      Annotation[] annotations,
+      MediaType mediaType,
+      MultivaluedMap<String, String> httpHeaders,
+      InputStream entityStream)
+      throws IOException, WebApplicationException {
+    return gson.fromJson(new BufferedReader(new 
InputStreamReader(entityStream)), type);
+  }
+
+  @Override
+  public boolean isWriteable(
+      Class<?> type, Type genericType, Annotation[] annotations, MediaType 
mediaType) {
+    return type != String.class; // Keep backward compatibility
+  }
+
+  @Override
+  public void writeTo(
+      T t,
+      Class<?> type,
+      Type genericType,
+      Annotation[] annotations,
+      MediaType mediaType,
+      MultivaluedMap<String, Object> httpHeaders,
+      OutputStream entityStream)
+      throws IOException, WebApplicationException {
+    try (PrintWriter printWriter = new PrintWriter(entityStream)) {
+      printWriter.write(gson.toJson(t));
+      printWriter.flush();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/0f9a1a80/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
index b699d2f..9e2c5f8 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
@@ -31,6 +31,9 @@ import org.apache.shiro.realm.text.IniRealm;
 import org.apache.shiro.web.env.EnvironmentLoaderListener;
 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
 import org.apache.shiro.web.servlet.ShiroFilter;
+import org.apache.zeppelin.rest.AdminRestApi;
+import org.apache.zeppelin.rest.exception.WebApplicationExceptionMapper;
+import org.apache.zeppelin.service.AdminService;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -429,6 +432,11 @@ public class ZeppelinServer extends Application {
   @Override
   public Set<Class<?>> getClasses() {
     Set<Class<?>> classes = new HashSet<>();
+
+    classes.add(GsonProvider.class);
+
+    classes.add(WebApplicationExceptionMapper.class);
+
     return classes;
   }
 
@@ -466,6 +474,11 @@ public class ZeppelinServer extends Application {
     ConfigurationsRestApi settingsApi = new ConfigurationsRestApi(notebook);
     singletons.add(settingsApi);
 
+    AdminService adminService = new AdminService();
+
+    AdminRestApi adminRestApi = new AdminRestApi(adminService);
+    singletons.add(adminRestApi);
+
     return singletons;
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/0f9a1a80/zeppelin-server/src/main/java/org/apache/zeppelin/service/AdminService.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/AdminService.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/AdminService.java
new file mode 100644
index 0000000..2d912e2
--- /dev/null
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/AdminService.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.zeppelin.service;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+import javax.ws.rs.BadRequestException;
+import org.apache.log4j.LogManager;
+import org.apache.zeppelin.rest.message.LoggerRequest;
+
+/** This class handles all of business logic of {@link 
org.apache.zeppelin.rest.AdminRestApi}. */
+public class AdminService {
+
+  public List<org.apache.log4j.Logger> getLoggers() {
+    Enumeration loggers = LogManager.getCurrentLoggers();
+    return StreamSupport.stream(
+            Spliterators.spliteratorUnknownSize(
+                new Iterator<org.apache.log4j.Logger>() {
+                  @Override
+                  public boolean hasNext() {
+                    return loggers.hasMoreElements();
+                  }
+
+                  @Override
+                  public org.apache.log4j.Logger next() {
+                    return 
org.apache.log4j.Logger.class.cast(loggers.nextElement());
+                  }
+                },
+                Spliterator.ORDERED),
+            false)
+        .collect(Collectors.toList());
+  }
+
+  public org.apache.log4j.Logger getLogger(String name) {
+    return LogManager.getLogger(name);
+  }
+
+  public void setLoggerLevel(LoggerRequest loggerRequest) throws 
BadRequestException {
+    try {
+      Class.forName(loggerRequest.getName());
+    } catch (Throwable ignore) {
+      throw new BadRequestException(
+          "The class of '" + loggerRequest.getName() + "' doesn't exists");
+    }
+
+    org.apache.log4j.Logger logger = 
LogManager.getLogger(loggerRequest.getName());
+    if (null == logger) {
+      throw new BadRequestException("The name of the logger is wrong");
+    }
+
+    org.apache.log4j.Level level = 
org.apache.log4j.Level.toLevel(loggerRequest.getLevel(), null);
+    if (null == level) {
+      throw new BadRequestException("The level of the logger is wrong");
+    }
+
+    logger.setLevel(level);
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/0f9a1a80/zeppelin-server/src/test/java/org/apache/zeppelin/service/AdminServiceTest.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/test/java/org/apache/zeppelin/service/AdminServiceTest.java
 
b/zeppelin-server/src/test/java/org/apache/zeppelin/service/AdminServiceTest.java
new file mode 100644
index 0000000..757e590
--- /dev/null
+++ 
b/zeppelin-server/src/test/java/org/apache/zeppelin/service/AdminServiceTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.zeppelin.service;
+
+import static org.junit.Assert.assertTrue;
+
+import org.apache.log4j.Level;
+import org.junit.Test;
+
+public class AdminServiceTest {
+
+  @Test
+  public void testSetLoggerLevel() {
+    AdminService adminService = new AdminService();
+    String testLoggerName = "test";
+    org.apache.log4j.Logger logger = adminService.getLogger(testLoggerName);
+    org.apache.log4j.Level level = logger.getLevel();
+    boolean setInfo = false;
+    if (org.apache.log4j.Level.INFO == level) {
+      // if a current level is INFO, set DEBUG to check if it's changed or not
+      logger.setLevel(org.apache.log4j.Level.DEBUG);
+    } else {
+      logger.setLevel(org.apache.log4j.Level.INFO);
+      setInfo = true;
+    }
+
+    logger = adminService.getLogger(testLoggerName);
+    assertTrue(
+        "Level of logger should be changed",
+        (setInfo && org.apache.log4j.Level.INFO == logger.getLevel())
+            || (!setInfo && Level.DEBUG == logger.getLevel()));
+  }
+}

Reply via email to