content: /*
   * Copyright 1999-2018 Alibaba Group Holding Ltd.
   *
   * 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 com.alibaba.druid.support.jakarta;
  
  import com.alibaba.druid.stat.DruidStatService;
  import com.alibaba.druid.support.logging.Log;
  import com.alibaba.druid.support.logging.LogFactory;
  import jakarta.servlet.ServletException;
  import jakarta.servlet.http.HttpServletRequest;
  import jakarta.servlet.http.HttpServletResponse;
  
  import javax.management.MBeanServerConnection;
  import javax.management.ObjectName;
  import javax.management.remote.JMXConnector;
  import javax.management.remote.JMXConnectorFactory;
  import javax.management.remote.JMXServiceURL;
  
  import java.io.IOException;
  import java.util.HashMap;
  import java.util.Map;
  
  /**
   * 注意：避免直接调用Druid相关对象例如DruidDataSource等，相关调用要到DruidStatManagerFacade里用反射实现
   *
   * @author sandzhang[sandzhangtoo@gmail.com]
   */
  public class StatViewServlet extends ResourceServlet {
      private static final Log LOG = LogFactory.getLog(StatViewServlet.class);
  
      private static final long serialVersionUID = 1L;
  
      public static final String PARAM_NAME_RESET_ENABLE = "resetEnable";
  
      public static final String PARAM_NAME_JMX_URL = "jmxUrl";
      public static final String PARAM_NAME_JMX_USERNAME = "jmxUsername";
      public static final String PARAM_NAME_JMX_PASSWORD = "jmxPassword";
  
      private DruidStatService statService = DruidStatService.getInstance();
  
      /**
       * web.xml中配置的jmx的连接地址
       */
      private String jmxUrl;
      /**
       * web.xml中配置的jmx的用户名
       */
      private String jmxUsername;
      /**
       * web.xml中配置的jmx的密码
       */
      private String jmxPassword;
      private MBeanServerConnection conn;
  
      public StatViewServlet() {
          super("support/http/resources");
      }
  
      public void init() throws ServletException {
          super.init();
  
          try {
              String param = getInitParameter(PARAM_NAME_RESET_ENABLE);
              if (param != null && param.trim().length() != 0) {
                  param = param.trim();
                  boolean resetEnable = Boolean.parseBoolean(param);
                  statService.setResetEnable(resetEnable);
              }
          } catch (Exception e) {
              String msg = "initParameter config error, resetEnable : " + getInitParameter(PARAM_NAME_RESET_ENABLE);
              LOG.error(msg, e);
          }
  
          // 获取jmx的连接配置信息
          String param = readInitParam(PARAM_NAME_JMX_URL);
          if (param != null) {
              jmxUrl = param;
              jmxUsername = readInitParam(PARAM_NAME_JMX_USERNAME);
              jmxPassword = readInitParam(PARAM_NAME_JMX_PASSWORD);
              try {
                  initJmxConn();
              } catch (IOException e) {
                  LOG.error("init jmx connection error", e);
              }
          }
  
      }
  
      /**
       * 读取servlet中的配置参数.
       *
       * @param key 配置参数名
       * @return 配置参数值，如果不存在当前配置参数，或者为配置参数长度为0，将返回null
       */
      private String readInitParam(String key) {
          String value = null;
          try {
              String param = getInitParameter(key);
              if (param != null) {
                  param = param.trim();
                  if (param.length() > 0) {
                      value = param;
                  }
              }
          } catch (Exception e) {
              String msg = "initParameter config [" + key + "] error";
              LOG.warn(msg, e);
          }
          return value;
      }
  
      /**
       * 初始化jmx连接
       *
       * @throws IOException
       */
      private void initJmxConn() throws IOException {
          if (jmxUrl != null) {
              JMXServiceURL url = new JMXServiceURL(jmxUrl);
              Map<String, String[]> env = null;
              if (jmxUsername != null) {
                  env = new HashMap<String, String[]>();
                  String[] credentials = new String[]{jmxUsername, jmxPassword};
                  env.put(JMXConnector.CREDENTIALS, credentials);
              }
              JMXConnector jmxc = JMXConnectorFactory.connect(url, env);
              conn = jmxc.getMBeanServerConnection();
          }
      }
  
      /**
       * 根据指定的url来获取jmx服务返回的内容.
       *
       * @param connetion jmx连接
       * @param url       url内容
       * @return the jmx返回的内容
       * @throws Exception the exception
       */
      private String getJmxResult(MBeanServerConnection connetion, String url) throws Exception {
          ObjectName name = new ObjectName(DruidStatService.MBEAN_NAME);
  
          String result = (String) conn.invoke(name, "service", new String[]{url},
                  new String[]{String.class.getName()});
          return result;
      }
  
      /**
       * 程序首先判断是否存在jmx连接地址，如果不存在，则直接调用本地的druid服务； 如果存在，则调用远程jmx服务。在进行jmx通信，首先判断一下jmx连接是否已经建立成功，如果已经
       * 建立成功，则直接进行通信，如果之前没有成功建立，则会尝试重新建立一遍。.
       *
       * @param url 要连接的服务地址
       * @return 调用服务后返回的json字符串
       */
      protected String process(String url) {
          String resp = null;
          if (jmxUrl == null) {
              resp = statService.service(url);
          } else {
              if (conn == null) {
                  // 连接在初始化时创建失败
                  try {
                      // 尝试重新连接
                      initJmxConn();
                  } catch (IOException e) {
                      LOG.error("init jmx connection error", e);
                      resp = DruidStatService.returnJSONResult(DruidStatService.RESULT_CODE_ERROR,
                              "init jmx connection error" + e.getMessage());
                  }
                  if (conn != null) {
                      // 连接成功
                      try {
                          resp = getJmxResult(conn, url);
                      } catch (Exception e) {
                          LOG.error("get jmx data error", e);
                          resp = DruidStatService.returnJSONResult(DruidStatService.RESULT_CODE_ERROR, "get data error:"
                                  + e.getMessage());
                      }
                  }
              } else {
                  // 连接成功
                  try {
                      resp = getJmxResult(conn, url);
                  } catch (Exception e) {
                      LOG.error("get jmx data error", e);
                      resp = DruidStatService.returnJSONResult(DruidStatService.RESULT_CODE_ERROR,
                              "get data error" + e.getMessage());
                  }
              }
          }
          return resp;
      }
  
      public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          String contextPath = request.getContextPath();
          String servletPath = request.getServletPath();
          String requestURI = request.getRequestURI();
  
          response.setCharacterEncoding("utf-8");
  
          if (contextPath == null) { // root context
              contextPath = "";
          }
          String uri = contextPath + servletPath;
          String path = requestURI.substring(contextPath.length() + servletPath.length());
  
          if ("".equals(path)) {
              if (contextPath.equals("") || contextPath.equals("/")) {
                  response.sendRedirect("/druid/index.html");
              } else {
                  response.sendRedirect("druid/index.html");
              }
              return;
          }
  
          if ("/".equals(path)) {
              response.sendRedirect("index.html");
              return;
          }
  
          super.service(request, response);
      }
  
  }
  