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

jadex.bridge.component.impl.ArgumentsResultsComponentFeature Maven / Gradle / Ivy

Go to download

Jadex bridge is a base package for kernels and platforms, i.e., it is used by both and provides commonly used interfaces and classes for active components and their management.

There is a newer version: 4.0.267
Show newest version
package jadex.bridge.component.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import jadex.base.IPlatformConfiguration;
import jadex.base.Starter;
import jadex.bridge.IComponentIdentifier;
import jadex.bridge.IComponentStep;
import jadex.bridge.IInternalAccess;
import jadex.bridge.component.ComponentCreationInfo;
import jadex.bridge.component.IArgumentsResultsFeature;
import jadex.bridge.component.IExecutionFeature;
import jadex.bridge.modelinfo.ConfigurationInfo;
import jadex.bridge.modelinfo.IArgument;
import jadex.bridge.modelinfo.UnparsedExpression;
import jadex.commons.IValueFetcher;
import jadex.commons.SReflect;
import jadex.commons.Tuple2;
import jadex.commons.collection.wrappers.MapWrapper;
import jadex.commons.future.Future;
import jadex.commons.future.IFuture;
import jadex.commons.future.ISubscriptionIntermediateFuture;
import jadex.commons.future.SubscriptionIntermediateFuture;
import jadex.commons.future.TerminationCommand;
import jadex.javaparser.IMapAccess;
import jadex.javaparser.SJavaParser;

/**
 *  This feature provides arguments.
 */
public class ArgumentsResultsComponentFeature extends AbstractComponentFeature	implements IArgumentsResultsFeature, IValueFetcher, IInternalArgumentsResultsFeature, IMapAccess
{
	//-------- attributes --------
	
	/** The arguments. */
	protected Map	arguments;
	
	/** The results. */
	protected Map	results;
	
	/** The result subscription, if any. */
	protected Set>>	resfuts;
	
	/** Flag to remember when an exception was notified to a listener. */
	protected boolean	notified;
	
	//-------- constructors --------
	
	/**
	 *  Create the feature.
	 */
	public ArgumentsResultsComponentFeature(IInternalAccess component, ComponentCreationInfo cinfo)
	{
		super(component, cinfo);
	}
	
	/**
	 *  Initialize the feature.
	 */
	public IFuture init()
	{
//		System.out.println("cl: "+getComponent().getClassLoader());
		
//		if(getComponent().getId().getName().toLowerCase().indexOf("intravma")!=-1)
//			System.out.println("here");
		
		// Init the arguments with parameters.
		if(cinfo.getArguments()!=null)
		{
			for(Iterator> it=cinfo.getArguments().entrySet().iterator(); it.hasNext(); )
			{
				Map.Entry entry = it.next();
				if(arguments==null)
					this.arguments	= new LinkedHashMap();
				arguments.put(entry.getKey(), entry.getValue());
			}
		}
		
		// Get the reverse name ([email protected] -> app1.agent1.)
		IComponentIdentifier cid = getComponent().getId();
		String dotname = cid.getDotName();
		int idx = dotname.lastIndexOf(".");
		if(idx!=-1)
		{
			dotname = dotname.substring(0, idx);
			dotname = getReverseName(dotname);
		}
		
		@SuppressWarnings("unchecked")
		Map	platformargs = (Map)Starter.getPlatformValue(getComponent().getId().getRoot(), IPlatformConfiguration.PLATFORMARGS);
		if(platformargs!=null)
		{
			IArgument[] margs = component.getModel().getArguments();
			for(int i=0; i();
					
					String argname = margs[i].getName();
					
//					if(dotname.toLowerCase().indexOf("cli")!=-1)
//						System.out.println("sdfhjsdf");
					
					// Test different versions of argument names (highest prio first), e.g. "arg" for agent [email protected]_123
					
					// 1) full application agent name (i.e. excluding platform), e.g. "b.a.arg"
					if(platformargs.containsKey(dotname+"."+argname))
					{
						arguments.put(argname, platformargs.get(dotname+"."+argname));
					}
					// 2a) local agent name dot arg, e.g. "a.arg"
					else if(platformargs.containsKey(cid.getLocalName()+"."+argname))
					{
						arguments.put(argname, platformargs.get(cid.getLocalName()+"."+argname));
					}
					// 2b) local agent name + arg, e.g. "aarg"
					else if(platformargs.containsKey(cid.getLocalName()+argname))
					{
						arguments.put(argname, platformargs.get(cid.getLocalName()+argname));
					}
					
					// 3) agent type name
					else if(platformargs.containsKey(getComponent().getModel().getName()+"."+argname))
					{
						arguments.put(argname, platformargs.get(getComponent().getModel().getName()+"."+argname));
					}
//					// todo: 4) agent type hierarchy name
//					else if(platformargs.containsKey(getComponent().getModel().getName()))
//					{
//						
//					}
					// 999...) name directly contained
					else if(platformargs.containsKey(argname))
					{
						arguments.put(argname, platformargs.get(argname));
					}
				}
			}
		}
		
		initDefaultArguments();
		
		// Hack?! add component identifier to result as long as we don't have better future type for results
		// could one somehow use the CallLocal for that purpose instead?
		this.results	= new MapWrapper(new LinkedHashMap())
		{
			protected void entryAdded(String key, Object value)
			{
				postEvent(key, value);
			}

			protected void entryRemoved(String key, Object value)
			{
				postEvent(key, null);
			}

			protected void entryChanged(String key, Object oldvalue, Object newvalue)
			{
				postEvent(key, newvalue);
			}
		};
		results.put(IComponentIdentifier.RESULTCID, getComponent().getId());
		
		initDefaultResults();
		
		return IFuture.DONE;
	}
	
	/**
	 *  Get the reverse name of a dot name (component id).
	 *  @return The reverse name as string.
	 */
	public static String getReverseName(String dotname)
	{
		List res = new ArrayList();
		StringTokenizer stok = new StringTokenizer(dotname, "@,");
		while(stok.hasMoreTokens())
		{
			res.add(0, stok.nextToken());
		}
		StringBuilder b = new StringBuilder();
		for(int i=0; i();
					arguments.put(upes[i].getName(), SJavaParser.getParsedValue(upes[i], component.getModel().getAllImports(), component.getFetcher(), component.getClassLoader()));
				}
			}
		}
		IArgument[] margs = component.getModel().getArguments();
		for(int i=0; i();
				
//				Class argclass = margs[i].getClazz().getType(getComponent().getClassLoader());
				if(margs[i].getDefaultValue().getValue()!=null && !arguments.containsKey(margs[i].getName()))
				{
					arguments.put(margs[i].getName(), SJavaParser.getParsedValue(margs[i].getDefaultValue(), component.getModel().getAllImports(), component.getFetcher(), component.getClassLoader()));
				}
				
				// Don't set basic type default values here, as they would overwrite java field initializer values.
				// Done lazy in get() instead.
//				else if(SReflect.isBasicType(argclass))
//				{
//					arguments.put(margs[i].getName(), SReflect.getDefaultValue(argclass));
//				}
			}
		}
	}
	
	/**
	 *  Init unset results from default values.
	 */
	protected void initDefaultResults()
	{
		// Init the results with initial or default values.
		ConfigurationInfo	ci	= component.getConfiguration()!=null ? component.getModel().getConfiguration(component.getConfiguration()) : null;
		
		if(ci!=null)
		{
			UnparsedExpression[]	upes	= ci.getResults();
			for(int i=0; i	shutdown()
	{
		doCleanup();
		
		return IFuture.DONE;
	}
	
	/**
	 *  Kill is only invoked, when shutdown of some (e.g. other) feature does not return due to timeout.
	 *  The feature should do any kind of possible cleanup, but no asynchronous operations.
	 */
	public void kill()
	{
		doCleanup();
	}
	
	/**
	 *  Perform cleanup in shutdown or kill.
	 */
	protected void doCleanup()
	{
		if(resfuts!=null)
		{
			Exception ex = getComponent().getException();
			if(ex!=null)
			{
				notified = true;
				for(SubscriptionIntermediateFuture> fut: resfuts)
				{
					fut.setExceptionIfUndone(ex);
				}
			}
			else
			{
//				System.out.println("setFinished "+getComponent().getId());
				for(SubscriptionIntermediateFuture> fut: resfuts)
				{
					fut.setFinishedIfUndone();
				}				
			}
			resfuts	= null;
		}
	}

	
	//-------- IValueFetcher interface --------
	
	/**
	 *  The feature can inject parameters for expression evaluation
	 *  by providing an optional value fetcher. The fetch order is the reverse
	 *  init order, i.e., later features can override values from earlier features.
	 */
	public IValueFetcher	getValueFetcher()
	{
		return this;
	}
	
	/**
	 *  Fetch the arguments.
	 */
	public Object fetchValue(String name)
	{
		Object	ret;
		if("$args".equals(name) || "$arguments".equals(name))
		{
			ret	= this;	// Use map access interface to provide default values for unset basic type args.
		}
		else
		{
			throw new RuntimeException("Value not found: "+name);
		}
		
		return ret;
	}
	
	//-------- IMapAccess --------
	
	/**
	 *  Provide default values for basic types, if not set. 
	 */
	public Object get(Object key)
	{
		Object	ret	= null;
		if(arguments!=null && arguments.containsKey(key))
		{
			ret	= arguments.get(key);
		}
		else if(key instanceof String)
		{
			IArgument	arg	=	getComponent().getModel().getArgument((String)key);
			if(arg!=null)
			{
				Class argclass = arg.getClazz().getType(getComponent().getClassLoader());
				if(SReflect.isBasicType(argclass))
				{
					ret	= SReflect.getDefaultValue(argclass);
				}
			}
		}
		return ret;
	}

	
	//-------- IArgumentsFeature interface --------
	
	/**
	 *  Get the arguments.
	 *  @return The arguments.
	 */
	public Map getArguments()
	{
		return arguments==null? Collections.emptyMap(): arguments;
	}
	
	/**
	 *  Get the current results.
	 *  @return The current result values (if any).
	 */
	public Map getResults()
	{
		return results;
	}
	
	/**
	 *  Get the arguments.
	 *  @return The arguments.
	 */
	public IFuture> getArgumentsAsync()
	{
		return new Future>(getArguments());
	}
	
	/**
	 *  Get the current results.
	 *  @return The current result values (if any).
	 */
	public IFuture> getResultsAsync()
	{
		return new Future>(getResults());
	}
	
	/**
	 *  Get the exception, if any.
	 *  @return The failure reason for use during cleanup, if any.
	 */
	public IFuture getExceptionAsync()
	{
		return new Future<>(component.getException());
	}
	
	/**
	 * Subscribe to receive results.
	 */
	public ISubscriptionIntermediateFuture> subscribeToResults()
	{
		if(resfuts==null)
			resfuts	= new LinkedHashSet>>();
		final SubscriptionIntermediateFuture>	ret	= new SubscriptionIntermediateFuture>();
		resfuts.add(ret);
		ret.setTerminationCommand(new TerminationCommand()
		{
			public void terminated(Exception reason)
			{
				if(getComponent().getFeature(IExecutionFeature.class).isComponentThread())
				{
					resfuts.remove(ret);
				}
				else
				{
					getComponent().getFeature(IExecutionFeature.class).scheduleStep(new IComponentStep()
					{
						public IFuture execute(IInternalAccess ia)
						{
							resfuts.remove(ret);
							return IFuture.DONE;
						}
					});
				}
			}
		});
		return ret;
	}

	//-------- internal interface --------
	
	/**
	 *  Check if there is somebody waiting for this component to finish.
	 *  Used to decide if a fatal error needs to be printed to the console.
	 */
	public boolean	exceptionNotified()
	{
		return notified;
	}

	//-------- helper methods --------
	
	/**
	 *  Post an event to subscribed listeners.
	 */
	protected void	postEvent(String result, Object value)
	{
		if(resfuts!=null)
		{
			Tuple2	event	= new Tuple2(result, value);
			for(SubscriptionIntermediateFuture> fut: resfuts)
			{
				fut.addIntermediateResultIfUndone(event);
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy