All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.parallelj.tracknrestart.aspects.QuartzContextAdapter.aj Maven / Gradle / Ivy

Go to download

ParallelJ is a Java framework for parallel computing. It provides flow modeling and execution. This project allows to deal with error management in ParallelJ and allows to track a Program execution, and restart it if some errors are found.

There is a newer version: 1.3.3
Show newest version
/*
 *     ParallelJ, framework for parallel computing
 *
 *     Copyright (C) 2010, 2011, 2012 Atos Worldline or third-party contributors as
 *     indicated by the @author tags or express copyright attribution
 *     statements applied by the authors.
 *
 *     This library is free software; you can redistribute it and/or
 *     modify it under the terms of the GNU Lesser General Public
 *     License as published by the Free Software Foundation; either
 *     version 2.1 of the License.
 *
 *     This library 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
 *     Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public
 *     License along with this library; if not, write to the Free Software
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
package org.parallelj.tracknrestart.aspects;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
//import java.util.concurrent.ExecutorService;
//import java.util.concurrent.Executors;

import org.apache.log4j.Logger;
import org.parallelj.Programs;
import org.parallelj.Programs.ProcessHelper;
import org.parallelj.internal.kernel.KCall;
import org.parallelj.internal.kernel.KProcess;
import org.parallelj.internal.reflect.ProgramAdapter.Adapter;
import org.parallelj.tracknrestart.ReturnCodes;
import org.parallelj.tracknrestart.annotations.TrackNRestart;
import org.parallelj.tracknrestart.databinding.ProgramFieldsBinder;
import org.parallelj.tracknrestart.listeners.ForEachListener;
import org.parallelj.tracknrestart.plugins.TrackNRestartPluginAll;
import org.parallelj.internal.kernel.procedure.RunnableProcedure;
import org.parallelj.internal.kernel.procedure.CallableProcedure;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobPersistenceException;

privileged public aspect QuartzContextAdapter percflow(execution(public void Job+.execute(..) throws JobExecutionException)) {
	
	declare precedence :
		org.parallelj.tracknrestart.aspects.QuartzContextAdapter,
		org.parallelj.launching.quartz.JobsAdapter;

	//---------------------------------------------------------------------------------------------------

	public static final String RETURN_CODE = "RETURN_CODE";

	static Logger logger = Logger.getLogger("org.parallelj.tracknrestart");
	
	private X root;

	String result = ReturnCodes.SUCCESS.name();
	
	//---------------------------------------------------------------------------------------------------
	
	declare parents:
		(@org.parallelj.launching.QuartzExecution *) implements Job;

	declare parents:           
		(org.quartz.Job+ && !org.quartz.Job) implements X;    

	declare parents:   
		(org.parallelj.internal.kernel.KCall) implements X;      

	//---------------------------------------------------------------------------------------------------
	
	public interface X {};
	
	private Throwable X.exceptionThrown = null;
	
	private ForEachListener X.forEachListener = null;       
	
	private String X.restartedFireInstanceId = null;       
	
	//---------------------------------------------------------------------------------------------------
	
	public void Job.execute(JobExecutionContext context) throws JobExecutionException {
		
//		ExecutorService service = null;
		
		try {
			logger.debug("-----------------------------------------------------------------------------------------------------");
			logger.debug("STARTING //J Root Program "+this.getClass().getName());
	
			context.setResult(new JobDataMap());

//			service = Executors.newFixedThreadPool(5);

//			ProcessHelper p = Programs.as((Adapter) this).execute(service).join();
			ProcessHelper p = Programs.as((Adapter) this).execute().join();

			ProgramFieldsBinder.getProgramOutputFields(this, context);

			X current = (X)this;
			if (current.exceptionThrown != null){
				throw new JobExecutionException(current.exceptionThrown);
			}
		} catch (IllegalAccessException e) {
			throw new JobExecutionException(e);
		} catch (NoSuchFieldException e) {
			throw new JobExecutionException(e);
		} finally {
			logger.debug("ENDING   //J Root Program "+this.getClass().getName());
			logger.debug("-----------------------------------------------------------------------------------------------------");
//			service.shutdown();
		}
	}

	//---------------------------------------------------------------------------------------------------
	
	//JobExecutionContext --> Job
	before(X self, JobExecutionContext jobExecutionContext): 
		execution(public void Job+.execute(..) throws JobExecutionException) 
		&& this(self) 
		&& args(jobExecutionContext) {

		self.restartedFireInstanceId = jobExecutionContext.getJobDetail().getJobDataMap().getString(TrackNRestartPluginAll.RESTARTED_FIRE_INSTANCE_ID);
		self.forEachListener = (ForEachListener)jobExecutionContext.getJobDetail().getJobDataMap().get(TrackNRestartPluginAll.FOR_EACH_LISTENER);

		this.root = self;
	}

	after(X self, JobExecutionContext jobExecutionContext): 
		execution(public void Job+.execute(..) throws JobExecutionException) 
		&& this(self) 
		&& args(jobExecutionContext) {

		Object oResult = jobExecutionContext.getResult();
		if (oResult instanceof JobDataMap){
			JobDataMap resultAsJobDataMap = (JobDataMap) oResult;
			resultAsJobDataMap.put(RETURN_CODE, this.result);
		} else {
			jobExecutionContext.setResult(this.result);
		}
	}
	
	after(X self): 
		execution(protected KCall.new(..)) && this(self) {

		self.restartedFireInstanceId = root.restartedFireInstanceId; 
		self.forEachListener = root.forEachListener;
	}

	after(X self): 
		execution(protected KProcess.new(..)) && this(self) {

		self.restartedFireInstanceId = root.restartedFireInstanceId; 
		self.forEachListener = root.forEachListener;
	}

	//---------------------------------------------------------------------------------------------------
	
    pointcut enter(KCall _kCall): call(* org.parallelj.internal.kernel.callback.Entry+.enter(KCall)) && args(_kCall);
    pointcut invoke(): call(public Object Method.invoke(Object, ..)) && !within(QuartzContextAdapter);

    // Runnable program. ----------------------------------------------------------------------------------------------------------------------------------

    declare parents:           
    	(java.lang.Runnable+ ) implements X, Y;    
    
	//---------------------------------------------------------------------------------------------------
	
    public interface Y {};
    
  	private Object Y.context = null;
  	
	//---------------------------------------------------------------------------------------------------
	
    // 1.1) Transfert from RunnableCall to Runnable. 
  	after(Object self) returning (Runnable ret): 
    	 execution(* org.parallelj.internal.kernel.procedure.RunnableProcedure.RunnableCall.toRunnable())
      && this(self)  {
    	
   	 ((X)ret).forEachListener = ((X)self).forEachListener;
   	 ((X)ret).restartedFireInstanceId = ((X)self).restartedFireInstanceId;
   	 ((Y)ret).context = ((org.parallelj.internal.kernel.KCall)self).context;
    }
    
    // 2.1) Transfert from CallableCall to Runnable. 
  	after(Object self) returning (Runnable ret): 
    	 execution(* org.parallelj.internal.kernel.procedure.CallableProcedure.CallableCall.toRunnable())
      && this(self)  {
    	
   	 ((X)ret).forEachListener = ((X)self).forEachListener;
   	 ((X)ret).restartedFireInstanceId = ((X)self).restartedFireInstanceId;
   	 ((Y)ret).context = ((org.parallelj.internal.kernel.KCall)self).context;
    }
  	
	public void skip(org.parallelj.internal.kernel.KCall kCall) {
		kCall.start();
		kCall.complete();
	}

    
    // 1.2) Interception around call of run (from RunnableCall).      
	void around(org.parallelj.internal.kernel.KProcessor.KProcessorRunnable self):
		call(public void run())
		&& target(org.parallelj.internal.kernel.procedure.RunnableProcedure.RunnableCall.RunnableCallRunnable)
		&& this(self) {
   	 
   	 Runnable runnable = ((org.parallelj.internal.kernel.KProcessor.KProcessorRunnable)self).getRunnable();
	 String restartedFireInstanceId = ((X)runnable).restartedFireInstanceId;
	 ForEachListener forEachListener = ((X)runnable).forEachListener;
   	 Object program =  ((Y)runnable).context;
   	 if (isToProcess(restartedFireInstanceId,forEachListener,program)) {
   			proceed(self);
   			Exception exec = ((RunnableProcedure.RunnableCall)((RunnableProcedure.RunnableCall.RunnableCallRunnable)thisJoinPoint.getTarget()).getRunnableCall()).getException();
   			if (exec == null) {
       			if (isTrackNRestartAnnoted(program)) {
    				String oid = getOID(program);
  					track(forEachListener, oid, true);
    			}
   			} else {
				if (isTrackNRestartAnnoted(program)){
					String oid = getOID(program);
					if (isTrackNRestartExceptionPermitted(program, exec)) {
						this.result = ReturnCodes.FAILURE.name();
	 					track(forEachListener, oid, false);
					} else {
						abortAbruptly(new TrackNRestartException("Exception thrown ("+exec.getClass().getName()+") is not in list of permitted exceptions "+filteredExceptionsAsString(getTrackNRestartAnnotedException(program))));
					}
				}
   			}
    	} else {
    		if (isTrackNRestartAnnoted(program)) {
				String oid = getOID(program);
				track(forEachListener, oid, true);
			} else {
				abortAbruptly(new TrackNRestartException("Unable to get iteration OID while 'tracking' context."));
			}
    		// Inform the engine that this Runnable has been called
    		skip(((RunnableProcedure.RunnableCall)((RunnableProcedure.RunnableCall.RunnableCallRunnable)thisJoinPoint.getTarget()).getRunnableCall()));
    	}
    }
    
    // 2.2) Interception around call of run (from CallableCall).      
	void around(org.parallelj.internal.kernel.KProcessor.KProcessorRunnable self):
		call(public void run())
		&& target(org.parallelj.internal.kernel.procedure.CallableProcedure.CallableCall.CallableCallRunnable)
		&& this(self) {
   	 
   	 Runnable runnable = ((org.parallelj.internal.kernel.KProcessor.KProcessorRunnable)self).getRunnable();
	 String restartedFireInstanceId = ((X)runnable).restartedFireInstanceId;
	 ForEachListener forEachListener = ((X)runnable).forEachListener;
   	 Object program =  ((Y)runnable).context;
   	 if (isToProcess(restartedFireInstanceId,forEachListener,program)) {
   			proceed(self);
   			Exception exec = ((CallableProcedure.CallableCall)((CallableProcedure.CallableCall.CallableCallRunnable)thisJoinPoint.getTarget()).getCallableCall()).getException();
   			if (exec == null) {
       			if (isTrackNRestartAnnoted(program)) {
    				String oid = getOID(program);
  					track(forEachListener, oid, true);
    			}
   			} else {
				if (isTrackNRestartAnnoted(program)){
					String oid = getOID(program);
					if (isTrackNRestartExceptionPermitted(program, exec)) {
						this.result = ReturnCodes.FAILURE.name();
	 					track(forEachListener, oid, false);
					} else {
						abortAbruptly(new TrackNRestartException("Exception thrown ("+exec.getClass().getName()+") is not in list of permitted exceptions "+filteredExceptionsAsString(getTrackNRestartAnnotedException(program))));
					}
				}
   			}
	 	} else {
			if (isTrackNRestartAnnoted(program)) {
				String oid = getOID(program);
				track(forEachListener, oid, true);
			} else {
				abortAbruptly(new TrackNRestartException("Unable to get iteration OID while 'tracking' context."));
			}
    		// Inform the engine that this Runnable has been called
			skip(((CallableProcedure.CallableCall)((CallableProcedure.CallableCall.CallableCallRunnable)thisJoinPoint.getTarget()).getCallableCall()));
		}
    }
    
 	//---------------------------------------------------------------------------------------------------
 	
     /**
      * Track the execution (success or failure).
      * @param forEachListener listener handling Quartz backend.
      * @param oid unique identifier
      * @param success boolean indicating if processing is a success or a failure.
      */
     private void track(ForEachListener forEachListener, String oid, boolean success) {
	     try {
				forEachListener.forEachInstanceComplete(oid, success);
	     } catch (JobPersistenceException e) {
				abortAbruptly(new TrackNRestartException("Unable to read status for iteration '"+ oid + "'.", e));
		} catch (SQLException e) {
				abortAbruptly(new TrackNRestartException("Unable to read status for iteration '"+ oid + "'.", e));
		}
     }
    
 
     // restart part ----------------------------------------------------------------------------------------------------------------------------------------
     
     Object around(Object oo, KCall _kCall)  :
        invoke() && args(oo, ..) && cflow(enter(_kCall)) {
         
       	if (isToProcess(((X)_kCall).restartedFireInstanceId, ((X)_kCall).forEachListener, oo)) {
               return proceed(oo, _kCall);
        } else {
               return null;
        }
     }

 	//---------------------------------------------------------------------------------------------------
 	
    /**
     * Check if an iterable must be processed. This means if the iterable is not already processed or the element was processed in error.
     * @param restartedFireInstanceId id of the batch to be restarted.
     * @param forEachListener listener handling request to Quartz. 
     * @param program instance of the program processing the iterable. 
     * @return true iterable has to be processed, false else.
     */
    private boolean isToProcess(String restartedFireInstanceId,  ForEachListener forEachListener, Object program)   {
		boolean result = true;
		if (program != null) {
			if (program.getClass().isAnnotationPresent(TrackNRestart.class)) {
				String oid = getOID(program);
				if (oid != null) {
					if (restartedFireInstanceId != null) {
						// logger.debug("Processing in restarting mode for oid : ["
						// + oid + "] ,program : [" + program +
						// "] ,instanceId :[" +instanceId+ "]");
						try {
							result = !forEachListener.isForEachInstanceIgnorable(restartedFireInstanceId, oid);
							// logger.debug(result ?
							// "This OID has not been already processed or" +
							// " already processed in error, so its processing is not skipped":
							// "This OID ("+oid+") has been already successfully processed, so its processing is skipped");
						} catch (JobPersistenceException e) {
							abortAbruptly(new TrackNRestartException("Unable to read status for iteration '"+ oid + "'.", e));
						} catch (SQLException e) {
							abortAbruptly(new TrackNRestartException("Unable to read status for iteration '"+ oid + "'.", e));
						}
					} else {
						// logger.debug("Processing in normal mode (ie not restarting mode) for : ["
						// + oid + "] ,program : [" + program + "]");
					}
				} else {
					abortAbruptly(new TrackNRestartException("Unable to get iteration OID while 'restarting' context."));
				}
			}
		}
		return result;
	}
    
    /**
     * Get unique identifier returned by the program (the program contains value to be processed).
     * @param program
     * @return unique identifier.
     */
	private String getOID(Object program) {
		String result = null;
		try {
			Method m = program.getClass().getDeclaredMethod("getOID", new Class[] {});
			return (String) m.invoke(program, new Object[] {});
		} catch (SecurityException e) {
			abortAbruptly(new TrackNRestartException(e));
		} catch (NoSuchMethodException e) {
			abortAbruptly(new TrackNRestartException("No getOID() method found while program " + program + " was annotated @TrackNRestart.", e));
		} catch (IllegalArgumentException e) {
			abortAbruptly(new TrackNRestartException(e));
		} catch (IllegalAccessException e) {
			abortAbruptly(new TrackNRestartException(e));
		} catch (InvocationTargetException e) {
			abortAbruptly(new TrackNRestartException(e));
		}
		
		return result;
	}
	
	/**
	 * Indicate of the Program contains annotation TrackNRestart.
	 * @param program
	 * @return true if program is annoted.
	 */
	private boolean isTrackNRestartAnnoted(Object program) {
		return program.getClass().isAnnotationPresent(TrackNRestart.class);
	}

	/**
	 * Check if the raised exception belong to the allowed ones in TrackNRestart annotation.
	 * @param program
	 * @param exception raised
	 * @return true if exception is allowed, false else.
	 */
	private boolean isTrackNRestartExceptionPermitted(Object program, Exception exception) {
		Class[] filteredExceptions =  getTrackNRestartAnnotedException(program);
		for (int i = 0; i < filteredExceptions.length; i++) {
				if (filteredExceptions[i].isAssignableFrom(exception.getClass())) {
					return true;
			}
		}
		return false;
	}
	
	/**
	 * Return exceptions defined in TrackNRestart annotation.
	 * @param program
	 * @return array of exceptions
	 */
	private Class[] getTrackNRestartAnnotedException (Object program) {
		return program.getClass().getAnnotation(TrackNRestart.class).filteredExceptions();
	}
	
	// tracking part ----------------------------------------------------------------------------------------------------------------------------------------

	after(Object oo, KCall _kCall) returning :
    	invoke() && args(oo, ..) && cflow(enter(_kCall)) {
		
		track(_kCall, oo, true, null);
    }

    after(Object oo, KCall _kCall) throwing (InvocationTargetException ite) : 
    	invoke() && args(oo, ..) && cflow(enter(_kCall)) {
		
		this.result = ReturnCodes.FAILURE.name();
		track(_kCall, oo, false, ite);
    }

	//---------------------------------------------------------------------------------------------------
	
	private void track(KCall _kCall, Object program, boolean success,
			InvocationTargetException ite) {
		String oid = null;

		try {
			if (program != null) {
				Class clazz = program.getClass();
				if (clazz.isAnnotationPresent(TrackNRestart.class)) {
					oid = getOID(program);
					if (oid != null) {
						if (success == true) {
							persist(_kCall, success, oid);
						} else {
							Class[] filteredExceptions = clazz
									.getAnnotation(TrackNRestart.class)
									.filteredExceptions();
							if (isIteCausePermittedInList(ite,
									filteredExceptions)) {
								persist(_kCall, success, oid);
							} else {
								abortAbruptly(
										new TrackNRestartException(
												"Exception thrown ("
												+ ite.getCause().getClass().getName()
												+ ") is not in list of permitted exceptions "
												+ filteredExceptionsAsString(filteredExceptions)));
							}
						}
						//if (logger.isDebugEnabled()) {
							// logger.debug("oid='" + oid +
							// "' persisted with status='" + (success ?
							// ReturnCodes.SUCCESS : ReturnCodes.FAILURE) + "' "+ (ite ==
							// null ? "" : ", cause : " + ite.getCause()));
						//}
					} else {
						abortAbruptly(new TrackNRestartException("Unable to get iteration OID while 'tracking' context."));
					}
				}
			}
		} catch (JobPersistenceException e) {
			abortAbruptly(new TrackNRestartException("Unable to persist status ['" + (success ? ReturnCodes.SUCCESS : ReturnCodes.FAILURE) + "'] for iteration '" + oid + "'.", e));
		} catch (SQLException e) {
			abortAbruptly(new TrackNRestartException("Unable to persist status ['" + (success ? ReturnCodes.SUCCESS : ReturnCodes.FAILURE) + "'] for iteration '" + oid + "'.", e));
		}
	}

	private boolean isIteCausePermittedInList(InvocationTargetException ite,
			Class[] filteredExceptions) {
		for (int i = 0; i < filteredExceptions.length; i++) {
			if (filteredExceptions[i].isAssignableFrom(ite.getCause()
					.getClass()))
				return true;
		}
		return false;
	}

	private String filteredExceptionsAsString(
			Class[] filteredExceptions) {
		StringBuffer sb = new StringBuffer();
		sb.append("[");
		for (int i = 0; i < filteredExceptions.length; i++) {
			if (i > 0)
				sb.append(", ");
			sb.append(filteredExceptions[i].getName());
		}
		sb.append("]");
		return sb.toString();
	}

	private void persist(KCall _kCall, boolean success, String oid)
			throws JobPersistenceException, SQLException {
		((X)_kCall).forEachListener.forEachInstanceComplete(oid, success);
	}

	private void abortAbruptly(Throwable t) {
		this.result = ReturnCodes.ABORTED.name();
		Programs.as((Adapter) this.root).abort();
		this.root.exceptionThrown = t;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy