diff --git a/README.rst b/README.rst index a7df4ec..838ff85 100644 --- a/README.rst +++ b/README.rst @@ -6,4 +6,5 @@ Sahara-extra is place for Sahara components not included into the main `Sahara r Here is the list of components: * Sources for Swift filesystem implementation for Hadoop: https://github.com/openstack/sahara-extra/blob/master/hadoop-swiftfs/README.rst +* Sources for main function wrapper that adapt for oozie: https://github.com/openstack/sahara-extra/blob/master/edp-adapt-for-oozie/README.rst * `Diskimage-builder `_ elements moved to the new repo: https://github.com/openstack/sahara-image-elements diff --git a/edp-adapt-for-oozie/README.rst b/edp-adapt-for-oozie/README.rst new file mode 100644 index 0000000..7540240 --- /dev/null +++ b/edp-adapt-for-oozie/README.rst @@ -0,0 +1,24 @@ +======================= +Sources for main function wrapper that adapt for oozie +======================= + +In order to pass configurations to MapReduce Application through oozie, +it is necessary to add the following code. +(https://github.com/openstack/sahara/blob/master/etc/edp-examples/edp-java/README.rst) + + // This will add properties from the tag specified + // in the Oozie workflow. For java actions, Oozie writes the + // configuration values to a file pointed to by ooze.action.conf.xml + conf.addResource(new Path("file:///", + System.getProperty("oozie.action.conf.xml"))); + +This wrapper adds a above configuration file to a default resources and +invoke actual main function. + +And this wrapper provides workaround for oozie's System.exit problem. +(https://oozie.apache.org/docs/4.0.0/WorkflowFunctionalSpec.html#a3.2.7_Java_Action) +In caller of oozie, System.exit is converted to exception. +The application can call System.exit multiple times. + +This wrapper stores the argument of System.exit called in first. +And return stored value if System.exit is called multiple times. diff --git a/edp-adapt-for-oozie/pom.xml b/edp-adapt-for-oozie/pom.xml new file mode 100644 index 0000000..c5a9150 --- /dev/null +++ b/edp-adapt-for-oozie/pom.xml @@ -0,0 +1,55 @@ + + + + 4.0.0 + + org.openstack.sahara.edp + edp-main-wrapper + 1.0.0-SNAPSHOT + EDP Java Action Main Wrapper for oozie + jar + + + UTF-8 + true + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + file://${basedir}/../hadoop-swiftfs/checkstyle.xml + false + xml + html + + + + + + diff --git a/edp-adapt-for-oozie/src/main/java/org/openstack/sahara/edp/MainWrapper.java b/edp-adapt-for-oozie/src/main/java/org/openstack/sahara/edp/MainWrapper.java new file mode 100644 index 0000000..e96d287 --- /dev/null +++ b/edp-adapt-for-oozie/src/main/java/org/openstack/sahara/edp/MainWrapper.java @@ -0,0 +1,91 @@ +package org.openstack.sahara.edp; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.Permission; +import java.util.Arrays; + +public class MainWrapper { + + public static void main(String[] args) throws Throwable { + + // Load oozie configuration file + String actionConf = System.getProperty("oozie.action.conf.xml"); + if (actionConf != null) { + Class configClass + = Class.forName("org.apache.hadoop.conf.Configuration"); + Method method = configClass.getMethod("addDefaultResource", String.class); + method.invoke(null, "action.xml"); + } + + SecurityManager originalSecurityManager = System.getSecurityManager(); + WrapperSecurityManager newSecurityManager + = new WrapperSecurityManager(originalSecurityManager); + System.setSecurityManager(newSecurityManager); + + Class mainClass = Class.forName(args[0]); + Method mainMethod = mainClass.getMethod("main", String[].class); + String[] newArgs = Arrays.copyOfRange(args, 1, args.length); + Throwable exception = null; + try { + mainMethod.invoke(null, (Object) newArgs); + } catch (InvocationTargetException e) { + if (!newSecurityManager.getExitInvoked()) { + exception = e.getTargetException(); + } + } + + System.setSecurityManager(originalSecurityManager); + + if (exception != null) { + throw exception; + } + if (newSecurityManager.getExitInvoked()) { + System.exit(newSecurityManager.getExitCode()); + } + } + + static class WrapperSecurityManager extends SecurityManager { + private static boolean exitInvoked = false; + private static int firstExitCode; + private SecurityManager securityManager; + + public WrapperSecurityManager(SecurityManager securityManager) { + this.securityManager = securityManager; + } + + @Override + public void checkPermission(Permission perm, Object context) { + if (securityManager != null) { + // check everything with the original SecurityManager + securityManager.checkPermission(perm, context); + } + } + + @Override + public void checkPermission(Permission perm) { + if (securityManager != null) { + // check everything with the original SecurityManager + securityManager.checkPermission(perm); + } + } + + @Override + public void checkExit(int status) throws SecurityException { + if (!exitInvoked) { + // save first System.exit status code + exitInvoked = true; + firstExitCode = status; + } + throw new SecurityException("Intercepted System.exit(" + status + ")"); + } + + public static boolean getExitInvoked() { + return exitInvoked; + } + + public static int getExitCode() { + return firstExitCode; + } + } +}