org.pepstock.jem.springbatch.tasks.utilities.MainLauncherTasklet.java Source code

Java tutorial

Introduction

Here is the source code for org.pepstock.jem.springbatch.tasks.utilities.MainLauncherTasklet.java

Source

/**
JEM, the BEE - Job Entry Manager, the Batch Execution Environment
Copyright (C) 2012-2015   Andrea "Stock" Stocchero
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.pepstock.jem.springbatch.tasks.utilities;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import javax.naming.NamingException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.pepstock.jem.annotations.AssignChunkContext;
import org.pepstock.jem.annotations.AssignStepContribution;
import org.pepstock.jem.annotations.SetFields;
import org.pepstock.jem.log.LogAppl;
import org.pepstock.jem.springbatch.SpringBatchMessage;
import org.pepstock.jem.springbatch.tasks.JemTasklet;
import org.pepstock.jem.springbatch.tasks.JobsProperties;
import org.pepstock.jem.springbatch.tasks.TaskletException;
import org.pepstock.jem.util.ClassLoaderUtil;
import org.pepstock.jem.util.ReverseURLClassLoader;
import org.pepstock.jem.util.UtilMessage;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.repeat.RepeatStatus;

/**
 * Is a JemTasklet which is able to execute java main class, having another classpath and arguments. 
 * Can accept a class name (to load).
 * 
 * @author Andrea "Stock" Stocchero
 * @version 2.2
 */
public final class MainLauncherTasklet extends JemTasklet {

    private static final String MAIN_METHOD = "main";

    private String className = null;

    private List<String> arguments = null;

    private List<String> classPath = null;

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.pepstock.jem.springbatch.tasks.JemTasklet#run(org.springframework
     * .batch.core.StepContribution,
     * org.springframework.batch.core.scope.context.ChunkContext)
     */
    @Override
    public RepeatStatus run(StepContribution stepContribution, ChunkContext chunkContext) throws TaskletException {
        if (className == null) {
            LogAppl.getInstance().emit(SpringBatchMessage.JEMS051E, "className");
            throw new TaskletException(SpringBatchMessage.JEMS051E.toMessage().getFormattedMessage("className"));
        }

        // load by Class.forName
        Class<?> clazz;
        // uses the class name
        try {
            // if there isn't classpath, loads class for name
            if (classPath == null || classPath.isEmpty()) {
                clazz = Class.forName(className);
            } else {
                // loads java class
                clazz = loadCustomClass(className);
            }
        } catch (ClassNotFoundException e) {
            LogAppl.getInstance().emit(SpringBatchMessage.JEMS052E, e, className);
            throw new TaskletException(SpringBatchMessage.JEMS052E.toMessage().getFormattedMessage(className), e);
        } catch (IOException e) {
            LogAppl.getInstance().emit(SpringBatchMessage.JEMS052E, e, className);
            throw new TaskletException(SpringBatchMessage.JEMS052E.toMessage().getFormattedMessage(className), e);
        }

        // checks if has got a public static void main
        if (hasMainMethod(clazz)) {
            try {
                // sets annotations here ONLY if no classpath is set
                if (classPath == null || classPath.isEmpty()) {
                    // replaces filed annotations
                    SetFields.applyByAnnotation(clazz);
                    applyByAnnotation(clazz, stepContribution, chunkContext);
                } else {
                    clazz = clazz.getClassLoader().loadClass(JavaMainClassLauncher.class.getName());
                    arguments.add(className);
                }
                // init params accordingly
                String[] params = null;
                // loads params
                if ((arguments != null) && (!arguments.isEmpty())) {
                    params = arguments.toArray(new String[0]);
                }

                // invokes main method
                Method main = clazz.getMethod(MAIN_METHOD, String[].class);
                main.invoke(null, (Object) params);
                return RepeatStatus.FINISHED;
            } catch (SecurityException e) {
                throw new TaskletException(e);
            } catch (IllegalArgumentException e) {
                throw new TaskletException(e);
            } catch (NoSuchMethodException e) {
                throw new TaskletException(e);
            } catch (IllegalAccessException e) {
                throw new TaskletException(e);
            } catch (InvocationTargetException e) {
                throw new TaskletException(e);
            } catch (NamingException e) {
                throw new TaskletException(e);
            } catch (ClassNotFoundException e) {
                throw new TaskletException(e);
            }
        } else {
            // throws an exception if is not a main class
            LogAppl.getInstance().emit(SpringBatchMessage.JEMS054E, className);
            throw new TaskletException(SpringBatchMessage.JEMS054E.toMessage().getFormattedMessage(className));
        }
    }

    /**
     * @return the className
     */
    public String getClassName() {
        return className;
    }

    /**
     * @param className the className to set
     */
    public void setClassName(String className) {
        this.className = className;
    }

    /**
     * @return the arguments
     */
    public List<String> getArguments() {
        return arguments;
    }

    /**
     * @param arguments the arguments to set
     */
    public void setArguments(List<String> arguments) {
        this.arguments = arguments;
    }

    /**
     * @return the classPath
     */
    public List<String> getClassPath() {
        return classPath;
    }

    /**
     * @param classPath the classPath to set
     */
    public void setClassPath(List<String> classPath) {
        this.classPath = classPath;
    }

    /**
     * Is a static method which checks if the passed class has got a
     * <code>main</code> method.
     * 
     * @param clazz class to be checked
     * @return <code>true</code> if the class has got the main method
     */
    private boolean hasMainMethod(Class<?> clazz) {
        try {
            Method method = clazz.getMethod(MAIN_METHOD, String[].class);
            return Modifier.isStatic(method.getModifiers());
        } catch (Exception e) {
            LogAppl.getInstance().ignore(e.getMessage(), e);
            return false;
        }
    }

    /**
     * Assigns the value of step contribution and chunk context 
     * @param clazz class for reflection
     * @param stepContribution step contribution, passed by SpringBatch core
     * @param chunkContext chunk context, passed by SpringBatch core
     * @throws IllegalAccessException if any error occurs
     */
    private void applyByAnnotation(Class<?> clazz, StepContribution stepContribution, ChunkContext chunkContext)
            throws IllegalAccessException {
        // scans all declared fields
        for (Field field : clazz.getDeclaredFields()) {
            // if has got data description annotation
            if (field.isAnnotationPresent(AssignStepContribution.class)
                    && Modifier.isStatic(field.getModifiers())) {
                FieldUtils.writeStaticField(field, stepContribution, true);
            } else if (field.isAnnotationPresent(AssignChunkContext.class)
                    && Modifier.isStatic(field.getModifiers())) {
                FieldUtils.writeStaticField(field, chunkContext, true);
            }
        }
    }

    /**
     * Loads java class from className and for classpath
     * @param className classname to be loaded
     * @return class object loaded from classpath
     * @throws IOException if any error occurs
     * @throws ClassNotFoundException if any error occurs
     */
    private Class<?> loadCustomClass(String classNam) throws IOException, ClassNotFoundException {
        // CLASSPATH has been set therefore it an try to load the plugin by
        // a custom classloader
        // collection of all file of classpath
        Collection<File> files = new LinkedList<File>();

        for (String classPathFile : classPath) {
            classPathFile = FilenameUtils.normalize(classPathFile, true);
            // checks if a item contains more than 1 path
            String[] paths = null;
            if (classPathFile.contains(File.pathSeparator)) {
                // substitute variables if there are and split
                paths = StringUtils.split(JobsProperties.getInstance().replacePlaceHolders(classPathFile),
                        File.pathSeparator);
            } else if (classPathFile.contains(";")) {
                // substitute variables if there are and split
                paths = StringUtils.split(JobsProperties.getInstance().replacePlaceHolders(classPathFile), ";");
            } else {
                // substitute variables if there are and assign
                paths = new String[] { JobsProperties.getInstance().replacePlaceHolders(classPathFile) };
            }
            if (paths != null) {
                for (String path : paths) {
                    // creates the file
                    File file = new File(path);
                    // if file ends with * could be only this folder or all folders
                    // in cascade
                    if (path.endsWith(ClassLoaderUtil.ALL_FOLDER)) {
                        // checks if is all folders in cascade
                        boolean cascade = path.endsWith(ClassLoaderUtil.ALL_FOLDER_IN_CASCADE);
                        // gets the parent and asks for all JAR files
                        File parent = file.getParentFile();
                        Collection<File> newFiles = FileUtils.listFiles(parent,
                                ClassLoaderUtil.EXTENSIONS.toArray(new String[0]), cascade);
                        // loads to the collection
                        files.addAll(newFiles);
                    } else if (file.isDirectory() && file.exists()) {
                        // if here, we have a directory
                        // adds the directory to collection
                        files.add(file);
                    } else if (file.isFile() && file.exists()) {
                        // if here, a file has been indicated
                        // adds the directory to collection
                        files.add(file);
                    }
                }
            }
        }
        // checks if the collection is empty.
        // if yes, all classpath definiton is wrong and no files have been
        // loaded
        if (!files.isEmpty()) {
            // gets where the class is located
            // because it must be added to classpath
            CodeSource codeSource = JavaMainClassLauncher.class.getProtectionDomain().getCodeSource();
            if (codeSource != null) {
                // gets URL
                URL url = codeSource.getLocation();
                if (url != null) {
                    // adds URL to classpath
                    files.add(FileUtils.toFile(url));
                }
            }
            // exports files in URLs, for our classloader
            final URL[] urls = FileUtils.toURLs(files.toArray(new File[files.size()]));
            // loads a our classloader by access controller
            ClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
                public ClassLoader run() {
                    return new ReverseURLClassLoader(urls, MainLauncherTasklet.class.getClassLoader(), false);
                }
            });
            // loads the plugin from classloader
            return loader.loadClass(className);
        } else {
            throw new IOException(UtilMessage.JEMB009E.toMessage().getMessage());
        }
    }
}