Java tutorial
/* * Copyright 2014 Cask Data, Inc. * * 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 co.cask.cdap.app.deploy; import co.cask.cdap.api.app.Application; import co.cask.cdap.api.app.ApplicationContext; import co.cask.cdap.app.ApplicationSpecification; import co.cask.cdap.app.DefaultAppConfigurer; import co.cask.cdap.app.program.Program; import co.cask.cdap.app.program.Programs; import co.cask.cdap.internal.app.ApplicationSpecificationAdapter; import co.cask.cdap.internal.io.ReflectionSchemaGenerator; import co.cask.cdap.security.ApplicationSecurity; import com.google.common.base.Charsets; import com.google.common.io.Files; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.io.FileUtils; import org.apache.hadoop.conf.Configuration; import org.apache.twill.filesystem.HDFSLocationFactory; import org.apache.twill.filesystem.LocalLocationFactory; import org.apache.twill.filesystem.LocationFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FilePermission; import java.io.IOException; import java.io.Writer; /** * Sandbox JVM allows the configuration phase of an application to be executed * within a contained JVM within minimal access to JVM capabilities. * <p> * Idea is that this piece of code is called in during the configuration phase * which happens during deployment and running in the same JVM as the server * could be dangerous. Hence, we spin-up a JVM with restricted access to resources * and invoke configure on application. * </p> */ public class SandboxJVM { private static final Logger LOG = LoggerFactory.getLogger(SandboxJVM.class); /** * Main class within the object. * * @param args specified on command line. * @return 0 if successfull; otherwise non-zero. */ public int doMain(String[] args) { String jarFilename; File outputFile; String locationFactory; String id; LocationFactory lf; CommandLineParser parser = new GnuParser(); Options options = new Options(); options.addOption("i", "id", true, "Account ID"); options.addOption("j", "jar", true, "Application JAR"); options.addOption("f", "locfactory", true, "Location Factory (LOCAL or DISTRIBUTED)"); options.addOption("o", "output", true, "Output"); // Check all the options of command line try { CommandLine line = parser.parse(options, args); if (!line.hasOption("jar")) { LOG.error("Application JAR not specified."); return -1; } if (!line.hasOption("output")) { LOG.error("Output file not specified."); return -1; } if (!line.hasOption("id")) { LOG.error("Account id not specified."); return -1; } if (!line.hasOption("locfactory")) { LOG.error("Location factory not specified."); return -1; } jarFilename = line.getOptionValue("jar"); outputFile = new File(line.getOptionValue("output")); id = line.getOptionValue("id"); locationFactory = line.getOptionValue("locfactory"); } catch (ParseException e) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("SandboxJVM", options); return -1; } // Load the JAR using the JAR class load and load the manifest file. File unpackedJarDir = Files.createTempDir(); try { Application app; try { if ("LOCAL".equals(locationFactory)) { lf = new LocalLocationFactory(); } else if ("DISTRIBUTED".equals(locationFactory)) { lf = new HDFSLocationFactory(new Configuration()); } else { LOG.error("Unknown location factory specified"); return -1; } Program archive = Programs.createWithUnpack(lf.create(jarFilename), unpackedJarDir); Object appMain = archive.getMainClass().newInstance(); if (!(appMain instanceof Application)) { LOG.error(String.format("Application main class is of invalid type: %s", appMain.getClass().getName())); return -1; } app = (Application) appMain; } catch (Exception e) { LOG.error(e.getMessage()); return -1; } // Now, we are ready to call configure on application. // Setup security manager, this setting allows only output file to be written. // Nothing else can be done from here on other than creating that file. ApplicationSecurity.builder().add(new FilePermission(outputFile.getAbsolutePath(), "write")).apply(); // Now, we call configure, which returns application specification. DefaultAppConfigurer configurer = new DefaultAppConfigurer(app); app.configure(configurer, new ApplicationContext()); ApplicationSpecification specification = configurer.createApplicationSpec(); // Convert the specification to JSON. // We write the Application specification to output file in JSON format. try { Writer writer = Files.newWriter(outputFile, Charsets.UTF_8); try { // TODO: The SchemaGenerator should be injected. ApplicationSpecificationAdapter.create(new ReflectionSchemaGenerator()).toJson(specification, writer); } finally { writer.close(); } } catch (IOException e) { LOG.error("Error writing to file {}. {}", outputFile, e.getMessage()); return -1; } } finally { try { FileUtils.deleteDirectory(unpackedJarDir); } catch (IOException e) { LOG.warn("Error deleting temp unpacked jar directory: {}", unpackedJarDir.getAbsolutePath(), e); } } return 0; } /** * Invoked from command line. * * @param args specified on command line. */ public static void main(String[] args) { SandboxJVM sandboxJVM = new SandboxJVM(); System.exit(sandboxJVM.doMain(args)); } }