Skip to content


Drools configuration with Spring. Invoke processes over AOP (Part 2)

Previous post describe how to configure processes using Spring context. This article is about how to invoke process. The simple way is to create Workflow process factory and simply load

public class WorkflowProcessFactory{
...
public <T extends WorkflowProcess> T loadProcess(final Class<T> clazz) throws IllegalArgumentException, InstantiationException, IllegalAccessException,
			InvocationTargetException, SecurityException, NoSuchMethodException, IOException
	{
		final Map<String, ProcessDescriptor> descr = sessionLookup.getProcessConfig();
		final ProcessDescriptor pd = descr.get(clazz.getSimpleName());
		final Constructor<T> constr = clazz.getConstructor(StatefulKnowledgeSession.class);
		final StatefulKnowledgeSession session = sessionLookup.newSession(clazz.getSimpleName());

		return constr.newInstance(session);
	}

And use it in future

WorkflowProcessFactory factory;
....
FirstWorkflowProcess process = factory.loadProcess(FirstWorkflowProcess.class);

process.setSetter1("string");
process.setSetter2(new Integer(23));
String return = process.execute();

For example: you already have a buisness method that should be extended by adding new functionality. As example we need to change other objects and send different notifications (emails). For this purpose  I use next class.

package com.company.workflow.integration;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Map;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.process.WorkItem;
import org.drools.runtime.process.WorkItemManager;

import com.company.annotations.WorkProcess;
import com.company.exception.DomainException;
import com.company.workflow.process.WorkflowProcess;
import com.company.workflow.workingitems.BaseWorkItemHandler;

@Aspect
public class WorkflowProcessFactory
{
	KnowledgeSessionLookup	sessionLookup;

	@SuppressWarnings("unchecked")
	@Around(value = "@annotation(com.company.annotations.WorkProcess) && @annotation(workProcess)", argNames = "workProcess")
	public Object executeProcess(final ProceedingJoinPoint joinPoint, final WorkProcess workProcess) throws Throwable
	{
		final Object[] args = joinPoint.getArgs();
		final String[] paramNames = workProcess.flowParameterNames();
		if (args.length != paramNames.length)
		{
			throw new IllegalArgumentException("WorkProcess:Method agruments length [" + args.length + "] doen't match annotation flowParameterNames length [" + paramNames.length
					+ "] for " + joinPoint.getSignature());
		}
		WorkflowProcess proc = null;
		int retIndex = -1;
		proc = createProcess(joinPoint, workProcess);
		if (paramNames.length > 0)
		{
			final Class clazz = proc.getClass();
			for (int i = 0; i < paramNames.length; i++)
			{
				final String paramName = paramNames[i];
				if (args[i] == null)
				{
					continue;
				}
				Class paramClass = args[i].getClass();
				Method setter = null;
				final String setterName = "set" + paramName.substring(0, 1).toUpperCase() + paramName.substring(1);
				while ((setter == null) && !Object.class.equals(paramClass))
				{
					try
					{
						setter = clazz.getMethod(setterName, paramClass);

					}
					catch (final java.lang.NoSuchMethodException ex)
					{
						paramClass = paramClass.getSuperclass();
					}
				}
				// Try interfaces
				if (setter == null)
				{
					final Class[] intfs = args[i].getClass().getInterfaces();
					if (intfs != null)
					{
						for (final Class intf : intfs)
						{
							try
							{
								setter = clazz.getMethod(setterName, new Class[] {
									intf
								});
								break;
							}
							catch (final NoSuchMethodException ex1)
							{
							}
						}
					}

				}
				if (setter == null)
				{
					throw new IllegalArgumentException("WorkProcess: Set method for  [" + paramNames[i] + "] with argument type " + args[i].getClass().getName() + " not found!");
				}
				setter.invoke(proc, args[i]);
				if (workProcess.returnName().equals(paramNames[i]))
				{
					retIndex = i;
				}
			}
		}

		final Object ret = proc.execute();
		// If wrapped execution of point cut proceeds inside work item handler
		// So return value of process
		if (workProcess.wrappedWorkItemName().length() > 0)
		{
			return ret;
		}
		if (retIndex > 0)
		{
			args[retIndex] = ret;
		}
		return joinPoint.proceed(args);
	}

	public WorkflowProcessFactory(final KnowledgeSessionLookup sessionLookup)
	{
		this.sessionLookup = sessionLookup;
	}

	@SuppressWarnings("unchecked")
	public <T extends WorkflowProcess> T createProcess(final ProceedingJoinPoint joinPoint, final WorkProcess workProcess) throws ClassNotFoundException
	{
		StatefulKnowledgeSession session;

		Class clazz = null;
		try
		{
			clazz = Class.forName(workProcess.value());
		}
		catch (final ClassNotFoundException ex)
		{
			clazz = Class.forName("com.company.workflow.process." + workProcess.value());
		}

		try
		{
			session = sessionLookup.newSession(clazz.getSimpleName());
		}
		catch (final IOException e)
		{
			throw new DomainException("Fail to create process '" + clazz.getSimpleName() + "'!", e);
		}

		T process = null;
		try
		{
			final Constructor<T> constr = clazz.getConstructor(StatefulKnowledgeSession.class);
			process = constr.newInstance(session);
			if (workProcess.wrappedWorkItemName().length() > 0)
			{
				// Point cut contains code that should be wrapped into Working Item.
				// Create working item handler.
				final WorkItemManager manager = session.getWorkItemManager();

				final BaseWorkItemHandler worker = new BaseWorkItemHandler()
				{

					@Override
					public void execute(final WorkItem workItem, final WorkItemManager manager)
					{
						try
						{
							final Object ret = joinPoint.proceed(joinPoint.getArgs());
							if (workProcess.returnName().length() > 0)
							{
								addResult(workProcess.returnName(), ret);
							}
							complete(workItem, manager);
						}
						catch (final Throwable e)
						{
							setException(workItem, new Exception(e));
							manager.abortWorkItem(workItem.getId());
						}
					}
				};
				worker.setSession(session);
				manager.registerWorkItemHandler(workProcess.wrappedWorkItemName(), worker);
			}
			// Inject static properties.
			final Map<String, ProcessDescriptor> descr = sessionLookup.getProcessConfig();
			final ProcessDescriptor pd = descr.get(clazz.getSimpleName());
			if (pd == null)
			{
				throw new Exception("Process '" + clazz.getSimpleName() + "' descriptor not found!");
			}
			for (final Map.Entry<String, Object> entry : pd.getBeans().entrySet())
			{
				final String setterName = "set" + entry.getKey().substring(0, 1).toUpperCase() + entry.getKey().substring(1);
				Method setter = null;
				try
				{
					setter = clazz.getMethod(setterName, new Class[] {
						entry.getValue().getClass()
					});
				}
				catch (final NoSuchMethodException ex)
				{
					final Class[] intfs = entry.getValue().getClass().getInterfaces();
					if (intfs == null)
					{
						throw ex;
					}
					for (final Class intf : intfs)
					{
						try
						{
							setter = clazz.getMethod(setterName, new Class[] {
								intf
							});
							break;
						}
						catch (final NoSuchMethodException ex1)
						{
						}
					}
				}
				if (setter == null)
				{
					throw new NoSuchMethodException(clazz.getName() + "(" + entry.getValue().getClass() + ")");
				}
				setter.invoke(process, entry.getValue());
			}
			return process;
		}
		catch (final Exception e)
		{
			throw new DomainException("Fail to create process '" + clazz.getSimpleName() + "'!", e);
		}
	}
}

Together with anntotation

package com.company.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface WorkProcess
{
	String value();

	String[] flowParameterNames() default {};

	String returnName() default "";

	String wrappedWorkItemName() default "";
}

How to use example (next post)

Posted in development.

Tagged with , , .


0 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

You must be logged in to post a comment.