content: /**
   * 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 com.alibaba.jstorm.daemon.supervisor;
  
  import java.io.BufferedReader;
  import java.io.BufferedWriter;
  import java.io.File;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.LineNumberReader;
  import java.io.PrintWriter;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.UUID;
  
  import org.apache.commons.lang.StringUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import backtype.storm.Config;
  import backtype.storm.utils.Utils;
  
  import com.alibaba.jstorm.client.ConfigExtension;
  import com.alibaba.jstorm.cluster.StormConfig;
  
  /**
   * Right now generate java sandbox policy through template file
   * In the future, generating java sandbox policy will through hard-code
   *
   * @author longda
   */
  public class SandBoxMaker {
      private static final Logger LOG = LoggerFactory.getLogger(SandBoxMaker.class);
  
      public static final String SANBOX_TEMPLATE_NAME = "sandbox.policy";
      public static final String JSTORM_HOME_KEY = "%JSTORM_HOME%";
      public static final String CLASS_PATH_KEY = "%CLASS_PATH%";
      public static final String LOCAL_DIR_KEY = "%JSTORM_LOCAL_DIR%";
  
      // this conf should only be supervisor conf
      private final Map conf;
      private final boolean isEnable;
      private final Map<String, String> replaceBaseMap = new HashMap<>();
  
      public SandBoxMaker(Map conf) {
          this.conf = conf;
          isEnable = ConfigExtension.isJavaSandBoxEnable(conf);
          LOG.info("Java Sandbox Policy :" + String.valueOf(isEnable));
  
          String jstormHome = System.getProperty("jstorm.home");
          if (jstormHome == null) {
              jstormHome = "./";
          }
  
          replaceBaseMap.put(JSTORM_HOME_KEY, jstormHome);
          replaceBaseMap.put(LOCAL_DIR_KEY, (String) conf.get(Config.STORM_LOCAL_DIR));
          LOG.info("JSTORM_HOME is " + jstormHome);
      }
  
      private String genClassPath(String classPathLine) {
          StringBuilder sb = new StringBuilder();
  
          String[] classpathList = classPathLine.split(":");
          for (String classpath : classpathList) {
              if (StringUtils.isBlank(classpath)) {
                  continue;
              }
  
              File file = new File(classpath);
              if (file.isDirectory()) {
                  sb.append(" permission java.io.FilePermission \"");
                  sb.append(classpath).append(File.separator).append("**");
                  sb.append("\", \"read\";\n");
              } else {
                  sb.append(" permission java.io.FilePermission \"");
                  sb.append(classpath);
                  sb.append("\", \"read\";\n");
              }
          }
  
          return sb.toString();
      }
  
      private String replaceLine(String line, Map<String, String> replaceMap) {
          for (Entry<String, String> entry : replaceMap.entrySet()) {
              if (line.contains(CLASS_PATH_KEY)) {
                  return genClassPath(entry.getValue());
              } else if (line.contains(entry.getKey())) {
                  return line.replace(entry.getKey(), entry.getValue());
              }
          }
  
          return line;
      }
  
      public String generatePolicyFile(Map<String, String> replaceMap) throws IOException {
          // dynamic generate policy file, no static file
          String tmpPolicy = StormConfig.supervisorTmpDir(conf) + File.separator + UUID.randomUUID().toString();
          InputStream inputStream = SandBoxMaker.class.getClassLoader().getResourceAsStream(SANBOX_TEMPLATE_NAME);
  
          PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(tmpPolicy)));
          BufferedReader reader = null;
          InputStreamReader inputReader = null;
          try {
              inputReader = new InputStreamReader(inputStream);
              reader = new BufferedReader(new LineNumberReader(inputReader));
              String line;
              while ((line = reader.readLine()) != null) {
                  String replaced = replaceLine(line, replaceMap);
                  writer.println(replaced);
              }
              return tmpPolicy;
          } catch (Exception e) {
              LOG.error("Failed to generate policy file\n", e);
              throw new IOException(e);
          } finally {
              if (inputStream != null) {
                  inputStream.close();
              }
              writer.close();
              if (reader != null) {
                  reader.close();
              }
              if (inputReader != null) {
                  inputReader.close();
              }
          }
      }
  
      /**
       * Generate command string
       */
      public String sandboxPolicy(String workerId, Map<String, String> replaceMap) throws IOException {
          if (!isEnable) {
              return "";
          }
  
          replaceMap.putAll(replaceBaseMap);
          String tmpPolicy = generatePolicyFile(replaceMap);
  
          File file = new File(tmpPolicy);
          String policyPath = StormConfig.worker_root(conf, workerId) + File.separator + SANBOX_TEMPLATE_NAME;
          File dest = new File(policyPath);
          file.renameTo(dest);
  
          StringBuilder sb = new StringBuilder();
          sb.append(" -Djava.security.manager -Djava.security.policy=");
          sb.append(policyPath);
  
          return sb.toString();
      }
  
      public static void main(String[] args) {
          Map<Object, Object> conf = Utils.readStormConfig();
          conf.put("java.sandbox.enable", true);
          SandBoxMaker maker = new SandBoxMaker(conf);
          try {
              System.out.println("sandboxPolicy:" + maker.sandboxPolicy("simple", new HashMap<String, String>()));
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
  