org.pepstock.jem.ant.tasks.StepListener.java Source code

Java tutorial

Introduction

Here is the source code for org.pepstock.jem.ant.tasks.StepListener.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.ant.tasks;

import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.net.UnknownHostException;
import java.rmi.RemoteException;
import java.util.Collection;
import java.util.Map;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Target;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.UnknownElement;
import org.apache.tools.ant.helper.AntXMLContext;
import org.apache.tools.ant.taskdefs.Java;
import org.pepstock.jem.Result;
import org.pepstock.jem.Step;
import org.pepstock.jem.ant.AntException;
import org.pepstock.jem.ant.AntKeys;
import org.pepstock.jem.ant.AntMessage;
import org.pepstock.jem.ant.DataDescriptionStep;
import org.pepstock.jem.log.LogAppl;
import org.pepstock.jem.node.DataPathsContainer;
import org.pepstock.jem.node.configuration.ConfigKeys;
import org.pepstock.jem.node.rmi.JobStartedObjects;
import org.pepstock.jem.node.rmi.TasksDoor;
import org.pepstock.jem.node.security.Role;
import org.pepstock.jem.node.tasks.JobId;
import org.pepstock.jem.util.Parser;
import org.pepstock.jem.util.rmi.RegistryLocator;
import org.pepstock.jem.util.rmi.RmiKeys;

/**
 * Implements the interfaces of ANT to listen all starts and ends both job and
 * steps.<br>
 * Inside ANT, steps are "targets".<br>
 * Communicates with JEM node passing all necessary information about job
 * starting and steps ending.
 * 
 * @author Andrea "Stock" Stocchero
 * 
 */
public class StepListener implements BuildListener {

    // due to we have to order the targets and for each targets
    // you can have more than 1 tasks, sets 100 as maximum number of tasks
    private static final int MAX_TASKS_FOR_TARGET = 100;

    // this is the FORK attribute name of JAVA ANT task
    // JAVA ANT task doesn't publish any get method to get it
    // and then it uses reflection to get it
    private static final String ANT_JAVA_TASK_FORK_ATTRIBUTE_NAME = "fork";

    private int stepOrder = MAX_TASKS_FOR_TARGET;

    private TasksDoor door = null;

    private boolean isFirst = true;

    private String lockingScope = AntKeys.ANT_JOB_SCOPE;

    private Locker locker = null;

    /**
     * Empty constructor
     */
    public StepListener() {
        ReturnCodesContainer.getInstance();
    }

    /**
     * Called by ANT engine when job is started.<br>
     * Notifies to JEM the job is starting, passing process id and receiving the
     * authorizations of job user instance.
     * 
     * @param event ANT event
     */
    @Override
    public void buildStarted(BuildEvent event) {
        if (door == null) {
            // get port number from env var
            // AntTask has passed and set this information (MUST)
            String port = System.getProperty(RmiKeys.JEM_RMI_PORT);
            try {
                // creates RMI connection with localhost (default) and using
                // port number.
                // if port is null or not a number, -1 is return and a RMI
                // Exception will occur
                RegistryLocator locator = new RegistryLocator(Parser.parseInt(port, -1));
                // check the taskdoor object is binded, if not, a exception
                // occurs
                if (locator.hasRmiObject(TasksDoor.NAME)) {
                    // gets remote object
                    door = (TasksDoor) locator.getRmiObject(TasksDoor.NAME);
                    // send to JEM node the current process id.
                    // uses JMX implementation of JDK.
                    // BE CAREFUL! Not all JVM returns the value in same format
                    // receives all roles for job user and stores in a static
                    // reference
                    // of realm
                    JobStartedObjects objects = door.setJobStarted(JobId.VALUE,
                            ManagementFactory.getRuntimeMXBean().getName());

                    // PAY attention: after creating data paths container
                    // calls a getabsolutepath method to load all necessary classes in classloader.
                    // This is MANDATORY to avoid StackOverFlow in the SecurityManager 
                    // during the CheckRead on files.
                    DataPathsContainer.createInstance(objects.getStorageGroupsManager());
                    DataPathsContainer.getInstance().getAbsoluteDataPath(JobId.VALUE);

                    // loads data paths as properties
                    int index = 0;
                    for (String dataName : DataPathsContainer.getInstance().getDataPathsNames()) {
                        String path = DataPathsContainer.getInstance().getDataPaths().get(index);
                        String property = ConfigKeys.JEM_DATA_PATH_NAME + "." + dataName;
                        System.setProperty(property, path);
                        index++;
                    }

                    Collection<Role> myroles = objects.getRoles();
                    // check if is already instantiated. If yes, does nothing
                    if (System.getSecurityManager() == null) {
                        System.setSecurityManager(new AntBatchSecurityManager(myroles));
                    } else {
                        throw new BuildException(AntMessage.JEMA039E.toMessage().getMessage());
                    }
                } else {
                    throw new BuildException(AntMessage.JEMA038E.toMessage().getFormattedMessage(TasksDoor.NAME));
                }
                // sets the SM for internal actions
                AntBatchSecurityManager batchSM = (AntBatchSecurityManager) System.getSecurityManager();
                // sets internal action to true so it can perform same authorized action
                batchSM.setInternalAction(true);
                // creates the locker to lock resources
                locker = new Locker();
                // sets internal action to false 
                batchSM.setInternalAction(false);

            } catch (AntException e) {
                throw new BuildException(AntMessage.JEMA040E.toMessage().getFormattedMessage(e.getMessage()), e);
            } catch (RemoteException e) {
                throw new BuildException(AntMessage.JEMA040E.toMessage().getFormattedMessage(e.getMessage()), e);
            } catch (UnknownHostException e) {
                throw new BuildException(AntMessage.JEMA040E.toMessage().getFormattedMessage(e.getMessage()), e);
            }
        }

    }

    /**
     * Called by ANT engine when job is ended.<br>
     * Notifies to JEM the job is ending, cleaning the subject loaded.
     * 
     * @param event ANT event
     */
    @Override
    public void buildFinished(BuildEvent event) {
        // sets the SM for internal actions
        AntBatchSecurityManager batchSM = (AntBatchSecurityManager) System.getSecurityManager();
        // sets internal action to true so it can perform same authorized action
        batchSM.setInternalAction(true);
        // flush STD OUT and ERR
        // probably is useless
        System.out.flush();
        System.err.flush();
        try {
            // calls node for job ened
            door.setJobEnded(JobId.VALUE);

            // if job locking is set, performs unlock
            if (isJobLockingScope()) {
                locker.unlock();
            }
        } catch (RemoteException e) {
            throw new BuildException(e.getMessage(), e);
        } catch (AntException e) {
            throw new BuildException(e.getMessage(), e);
        } finally {
            // sets onternal action to false
            batchSM.setInternalAction(false);
        }
    }

    /**
     * Called by ANT engine when a step is started.<br>
     * Notifies to JEM the current step in execution to save inside of JEM job
     * instance.
     * 
     * @param event ANT event
     */
    @Override
    public void targetStarted(BuildEvent event) {
        // checks if is the first step
        // if yes, does many activities:
        // - loading all targets and task
        // - prepares for resources locking
        if (isFirst) {
            // sets locking scope
            setLockingScope(event.getProject());
            event.getProject().log(AntMessage.JEMA033I.toMessage().getFormattedMessage(lockingScope));

            // if Job scope is set, loads all items to lock resources
            if (isJobLockingScope()) {
                loadForLock(event.getProject());
                // check procedure 
                checkProcedure();
                try {
                    locker.lock();
                } catch (AntException e) {
                    throw new BuildException(e);
                }
            }
            isFirst = false;
        }

        // if step locking is set, then locks resources
        // loading tasks info
        if (isStepLockingScope()) {
            loadForLock(event.getTarget());
            checkProcedure();
            try {
                locker.lock();
            } catch (AntException e) {
                throw new BuildException(e);
            }
        }

        AntBatchSecurityManager batchSM = (AntBatchSecurityManager) System.getSecurityManager();
        batchSM.setInternalAction(true);

        // checks if the target name is empty. if yes, does nothing
        if (!"".equals(event.getTarget().getName())) {
            try {
                // creates object to send to JEM
                Step step = new Step();
                // sets step name and description
                step.setName(event.getTarget().getName());
                step.setDescription(event.getTarget().getDescription());

                // send to JEM by RMI
                door.setStepStarted(JobId.VALUE, step);
            } catch (RemoteException e) {
                throw new BuildException(e.getMessage(), e);
            } finally {
                batchSM.setInternalAction(false);
            }
        }
        batchSM.setInternalAction(false);

    }

    /**
     * Called by ANT engine when a step is ended.<br>
     * Notifies to JEM a summary about step execution (i.e. return-code,
     * exception).
     * 
     * @param event ANT event
     */
    @Override
    public void targetFinished(BuildEvent event) {
        AntBatchSecurityManager batchSM = (AntBatchSecurityManager) System.getSecurityManager();
        batchSM.setInternalAction(true);

        // checks if the target name is empty. if yes, does nothing
        if (!"".equals(event.getTarget().getName())) {
            try {
                // creates object to send to JEM
                Step step = new Step();
                // sets step name and description
                step.setName(event.getTarget().getName());
                step.setDescription(event.getTarget().getDescription());

                // checks if has an exception.If yes, sets ERROR, otherwise
                // SUCCESS
                step.setReturnCode((event.getException() != null) ? Result.ERROR : Result.SUCCESS);
                // checks if has an exception, sets the exception message
                if (event.getException() != null) {
                    step.setException(event.getException().getMessage());
                }

                // send to JEM by RMI
                door.setStepEnded(JobId.VALUE, step);

                // if setp locking scope is st, unlocks resources
                if (isStepLockingScope()) {
                    locker.unlock();
                }

            } catch (AntException e) {
                throw new BuildException(e);
            } catch (RemoteException e) {
                throw new BuildException(e);
            } finally {
                batchSM.setInternalAction(false);
            }
        }
        batchSM.setInternalAction(false);
    }

    /**
     * If a task locking scope is set, load resources definition and asks for locking
     * 
     * @param event ANT event
     */
    @Override
    public void taskStarted(BuildEvent event) {
        // checks if you are using a JAVA ANT task with FORK
        // this option is not allowed because with the fork
        // the application is out of secured environment,
        // that means without security manager

        // checks if is cast-able
        if (event.getTask() != null) {
            Task task = null;
            // checks if is an Unknown element
            if (event.getTask() instanceof UnknownElement) {
                // gets ANT task
                UnknownElement ue = (UnknownElement) event.getTask();
                ue.maybeConfigure();
                task = (Task) ue.getTask();
            } else if (event.getTask() instanceof Task) {
                // gets the task
                // here if the ANT task is already configured
                // mainly on sequential, parallel and JEM procedure
                task = (Task) event.getTask();
            }
            // if is a ANT JAVA TASK
            if (task instanceof Java && !(task instanceof StepJava)) {
                // gets AJAV task
                Java java = (Java) task;
                boolean isFork = true;
                try {
                    // reflection to understand if the attribute fork is set to true
                    // unfortunately ANT java task don't have any get method to have fork value
                    Field f = java.getClass().getDeclaredField(ANT_JAVA_TASK_FORK_ATTRIBUTE_NAME);
                    isFork = (Boolean) FieldUtils.readField(f, java, true);
                } catch (SecurityException e) {
                    LogAppl.getInstance().ignore(e.getMessage(), e);
                } catch (NoSuchFieldException e) {
                    LogAppl.getInstance().ignore(e.getMessage(), e);
                } catch (IllegalAccessException e) {
                    LogAppl.getInstance().ignore(e.getMessage(), e);
                }
                // and force FORK to false
                java.setFork(false);
                if (isFork) {
                    // shows the message of the force of fork.
                    event.getProject().log(
                            AntMessage.JEMA077W.toMessage().getFormattedMessage(event.getTask().getTaskName()));
                }
            }

        }

        // if task locking scope is set, locks resources
        if (isTaskLockingScope()) {
            loadForLock(event.getTask());
            checkProcedure();
            try {
                locker.lock();
            } catch (AntException e) {
                throw new BuildException(e);
            }
        }

    }

    /**
     * If a task locking scope is set, asks for unlocking
     * 
     * @param event ANT event
     */
    @Override
    public void taskFinished(BuildEvent event) {
        // if task locking scope is set, unlocks resources
        if (isTaskLockingScope()) {
            try {
                locker.unlock();
            } catch (AntException e) {
                throw new BuildException(e);
            }
        }
    }

    /**
     * Not implemented.
     * 
     * @param event ANT event
     */
    @Override
    public void messageLogged(BuildEvent event) {
        // nop
    }

    /**
     * @return <code>true</code> if locking scope is set to JOB
     */
    private boolean isJobLockingScope() {
        return lockingScope.equalsIgnoreCase(AntKeys.ANT_JOB_SCOPE);
    }

    /**
     * @return <code>true</code> if locking scope is set to STEP
     */
    private boolean isStepLockingScope() {
        return lockingScope.equalsIgnoreCase(AntKeys.ANT_STEP_SCOPE);
    }

    /**
     * @return <code>true</code> if locking scope is set to TASK
     */
    private boolean isTaskLockingScope() {
        return lockingScope.equalsIgnoreCase(AntKeys.ANT_TASK_SCOPE);
    }

    /**
     * Reads the parameters and sets the locking scope
     * @param project JOB
     */
    private void setLockingScope(Project project) {
        // if locking is not present, uses default
        String lockingScopeProperty = project.getProperty(AntKeys.ANT_LOCKING_SCOPE);
        if (lockingScopeProperty == null) {
            return;
        }

        // checks the parameter has a correct value
        // if not, throws an exception
        if (!lockingScopeProperty.equalsIgnoreCase(AntKeys.ANT_JOB_SCOPE)
                && !lockingScopeProperty.equalsIgnoreCase(AntKeys.ANT_STEP_SCOPE)
                && !lockingScopeProperty.equalsIgnoreCase(AntKeys.ANT_TASK_SCOPE)) {
            throw new BuildException(AntMessage.JEMA032E.toMessage().getFormattedMessage(AntKeys.ANT_LOCKING_SCOPE,
                    lockingScopeProperty));
        }
        lockingScope = lockingScopeProperty.toLowerCase();
    }

    /**
     * Reads all project loading all targets and tasks
     * @param project JOB
     */
    @SuppressWarnings("rawtypes")
    private void loadForLock(Project project) {
        Map mm = project.getCopyOfReferences();
        // property of ProjectHelper2.REFID_CONTEXT but is not visible
        AntXMLContext context = (AntXMLContext) mm.get("ant.parsing.context");

        for (Object obj : context.getTargets()) {
            Target target = (Target) obj;
            loadForLock(target);
            // every target starts from a new hundreds
            stepOrder = stepOrder + MAX_TASKS_FOR_TARGET;
        }
    }

    /**
     * Loads all tasks for a specific target
     * @param target STEP
     */
    private void loadForLock(Target target) {
        Task[] tasks = target.getTasks();
        if ((tasks != null) && (tasks.length > 0)) {
            for (int i = 0; i < tasks.length; i++) {
                // load tasks info setting the order 
                loadForLock(tasks[i], stepOrder + i);
            }
        }
    }

    /**
     * Called only when the locking scope is TASk, so order is always 0 (no sort needed)
     * @param task TASK
     */
    private void loadForLock(Task task) {
        loadForLock(task, 0);
    }

    /**
     * Called to loads all defined tasks specific target, setting the order
     * @param task TASK
     * @param order order of step/task
     */
    private void loadForLock(Task task, int order) {
        if (task instanceof UnknownElement) {
            UnknownElement uePre = (UnknownElement) task;
            UnknownElement ue;
            try {
                // must object to be cloned
                // otherwise some actions (like ANT variables substitutions)
                // are not applied on JCL
                ue = (UnknownElement) uePre.clone();
                ue.maybeConfigure();

                if (ue.getTask() instanceof DataDescriptionStep) {
                    DataDescriptionStep item = (DataDescriptionStep) ue.getTask();
                    item.setOrder(order);
                    StepsContainer.getInstance().getDataDescriptionSteps().add(item);
                } else if (ue.getTask() instanceof Procedure) {
                    Procedure proc = (Procedure) ue.getTask();
                    proc.setOrder(order);
                    ProceduresContainer.getInstance().getProcedures().add(proc);
                } else if (ue.getRealThing() instanceof ProcedureDefinition) {
                    ProcedureDefinition def = (ProcedureDefinition) ue.getRealThing();
                    ProceduresContainer.getInstance().getProceduresDefinitions().put(def.getName(), def);
                }
            } catch (CloneNotSupportedException e) {
                // debug
                LogAppl.getInstance().debug(e.getMessage(), e);
            }

        }
    }

    /**
     * loads all information for the procedures
     */
    private void checkProcedure() {
        if (ProceduresContainer.getInstance().getProcedures().isEmpty()) {
            return;
        }

        // scans defined procedures
        for (Procedure proc : ProceduresContainer.getInstance().getProcedures()) {
            if (proc.getName() != null) {
                // gets procedure
                ProcedureDefinition pd = ProceduresContainer.getInstance().getProceduresDefinitions()
                        .get(proc.getName());
                // saves ProcDef
                proc.setDefinition(pd);
                // loads tasks overriding the nested
                proc.loadDefinedTask();
                proc.ovverrideDefinedTask();

                // adds all dat description step for lockings
                for (Task task : proc.getDefinedTasks()) {
                    if (task instanceof DataDescriptionStep) {
                        DataDescriptionStep item = (DataDescriptionStep) task;
                        item.setOrder(proc.getOrder());
                        StepsContainer.getInstance().getDataDescriptionSteps().add(item);
                    }
                }
            }
        }
    }

}