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

jadex.bridge.SFuture 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;

import java.util.stream.Stream;

import jadex.base.Starter;
import jadex.bridge.component.IExecutionFeature;
import jadex.bridge.service.annotation.Timeout;
import jadex.bridge.service.component.ComponentFutureFunctionality;
import jadex.bridge.service.component.interceptors.FutureFunctionality;
import jadex.bridge.service.search.ServiceEvent;
import jadex.bridge.service.types.registry.SlidingCuckooFilter;
import jadex.commons.IResultCommand;
import jadex.commons.SUtil;
import jadex.commons.future.Future;
import jadex.commons.future.IForwardCommandFuture;
import jadex.commons.future.IFuture;
import jadex.commons.future.IIntermediateFuture;
import jadex.commons.future.IPullIntermediateFuture;
import jadex.commons.future.IPullSubscriptionIntermediateFuture;
import jadex.commons.future.ISubscriptionIntermediateFuture;
import jadex.commons.future.ITerminableFuture;
import jadex.commons.future.ITerminableIntermediateFuture;
import jadex.commons.future.ITuple2Future;
import jadex.commons.future.IntermediateFuture;
import jadex.commons.future.PullIntermediateDelegationFuture;
import jadex.commons.future.PullSubscriptionIntermediateDelegationFuture;
import jadex.commons.future.SubscriptionIntermediateDelegationFuture;
import jadex.commons.future.TerminableDelegationFuture;
import jadex.commons.future.TerminableIntermediateDelegationFuture;
import jadex.commons.future.Tuple2Future;

/**
 *  Helper class for future aspects.
 */
public class SFuture
{
	/**
	 *  Create an intermediate future for a stream.
	 *  The results are pulled from the stream using the agent thread i.e. the agent will be blocked when waiting for stream results.
	 *  Safe to use (but somewhat useless) for finished streams.
	 *  Also safe to use for streams, created with IntermediateFuture.asStream().
	 *  Not safe to use for other kinds of infinite streams!
	 */
	public static  IntermediateFuture streamToFuture(IInternalAccess agent, Stream results)
	{
		// Asynchronously transform results, otherwise method would block before returning stream-connected future
		// and results would only be sent in bunch at the end or never, if the source future doesn't finish.
		IntermediateFuture	ret	= new IntermediateFuture<>();
		agent.scheduleStep(ia ->
		{
			results.forEach(item -> ret.addIntermediateResult(item));
			ret.setFinished();
			return IFuture.DONE;
		});
		return ret;
	}
	
	/**
	 *  Automatically update the timer of a long running service call future.
	 *  Ensures that the caller does not timeout even if no result
	 *  value is set in that time span.
	 *  The call periodically sends alive calls to the caller. 
	 *  @param ret The future that is returned by the service call.
	 *  @param ia The component handling the service call (on that component the periodic updates are scheduled).
	 */
	public static void avoidCallTimeouts(final Future ret, IInternalAccess ia)
	{
		ServiceCall sc = ServiceCall.getCurrentInvocation();
//		boolean	realtime	= sc!=null ? sc.getRealtime()!=null ? sc.getRealtime().booleanValue() : false : false;
		boolean	realtime = sc != null ? sc.isRemoteCall(ia.getId()) : false;
		avoidCallTimeouts(ret, ia, Starter.isRealtimeTimeout(ia.getId(), realtime));
	}
	
	/**
	 *  Automatically update the timer of a long running service call future.
	 *  Ensures that the caller does not timeout even if no result
	 *  value is set in that time span.
	 *  The call periodically sends alive calls to the caller. 
	 *  @param ret The future that is returned by the service call.
	 *  @param ia The component handling the service call (on that component the periodic updates are scheduled).
	 *  @param realtime	true, for real time timeouts (simulation clock based timeouts otherwise).
	 */
	public static void avoidCallTimeouts(final Future ret, IInternalAccess ia, boolean realtime)
	{
		ServiceCall sc = ServiceCall.getCurrentInvocation();
		long to = sc!=null? sc.getTimeout(): Starter.getDefaultTimeout(ia.getId()); // Hack!!! find out in which cases service call can null
	//	boolean local = sc.getCaller().getPlatformName().equals(agent.getComponentIdentifier().getPlatformName());
	//	long to = sc.getTimeout()>0? sc.getTimeout(): (local? BasicService.DEFAULT_LOCAL: BasicService.DEFAULT_REMOTE);
	//	to = 5000;
		avoidCallTimeouts(ret, ia, to, realtime);
	}
	
	/**
	 *  Automatically update the timer of a long running service call future.
	 *  Ensures that the caller does not timeout even if no result
	 *  value is set in that timespan.
	 *  The call periodically sends alive calls to the caller. 
	 *  @param ret The future that is returned by the service call.
	 *  @param ea The component handling the service call (on that component the periodic updates are scheduled).
	 */
	public static void avoidCallTimeouts(final Future ret, IExternalAccess ea)
	{
		ServiceCall sc = ServiceCall.getCurrentInvocation();
		long to = sc!=null? sc.getTimeout(): Starter.getDefaultTimeout(ea.getId()); // Hack!!! find out in which cases service call can null
//		boolean	realtime	= sc!=null ? sc.getRealtime()!=null ? sc.getRealtime().booleanValue() : false : false;
		boolean	realtime = sc != null ? sc.isRemoteCall(ea.getId()) : false;
	//	boolean local = sc.getCaller().getPlatformName().equals(agent.getComponentIdentifier().getPlatformName());
	//	long to = sc.getTimeout()>0? sc.getTimeout(): (local? BasicService.DEFAULT_LOCAL: BasicService.DEFAULT_REMOTE);
	//	to = 5000;
		avoidCallTimeouts(ret, ea, to, Starter.isRealtimeTimeout(ea.getId(), realtime));
	}
		
	/**
	 *  Automatically update the timer of a long running service call future.
	 *  Ensures that the caller does not timeout even if no result
	 *  value is set in that timespan.
	 *  The call periodically sends alive calls to the caller. 
	 *  @param ret The future that is returned by the service call.
	 *  @param ia The component handling the service call (on that component the periodic updates are scheduled).
	 *  @param to The timeout.
	 */
	public static void avoidCallTimeouts(final Future ret, IInternalAccess ia, long to, boolean realtime)
	{
		avoidCallTimeouts(ret, ia, to, 0.8, realtime);
	}
	
	/**
	 *  Automatically update the timer of a long running service call future.
	 *  Ensures that the caller does not timeout even if no result
	 *  value is set in that timespan.
	 *  The call periodically sends alive calls to the caller. 
	 *  @param ret The future that is returned by the service call.
	 *  @param ea The component handling the service call (on that component the periodic updates are scheduled).
	 *  @param to The timeout.
	 */
	public static void avoidCallTimeouts(final Future ret, IExternalAccess ea, long to, boolean realtime)
	{
		avoidCallTimeouts(ret, ea, to, 0.8, realtime);
	}
	
	/**
	 *  Automatically update the timer of a long running service call future.
	 *  Ensures that the caller does not timeout even if no result
	 *  value is set in that timespan.
	 *  The call periodically sends alive calls to the caller. 
	 *  @param ret The future that is returned by the service call.
	 *  @param ia The component handling the service call (on that component the periodic updates are scheduled).
	 *  @param to The timeout.
	 *  @param factor (default 0.8) Used to update the timer when factor*to has elapsed.
	 */
	public static void avoidCallTimeouts(final Future ret, IInternalAccess ia, long to, double factor, final boolean realtime)
	{
		if(to>0)
		{
			final long w = (long)(to*factor);
			IComponentStep step = new IComponentStep()
//			IComponentStep step = new ImmediateComponentStep()
			{
				public IFuture execute(IInternalAccess ia)
				{
					if(!ret.isDone())
					{
						ret.sendForwardCommand(IForwardCommandFuture.Type.UPDATETIMER);
						ia.getFeature(IExecutionFeature.class).waitForDelay(w, this, realtime);
					}
					return IFuture.DONE;
				}
			};
//			ia.getFeature(IExecutionFeature.class).waitForDelay(w, step, realtime);
			
			// Send the first update immediately since the avoid is set up at
			// the receiver and some time may have already passed until the receiver
			// gets the call. Otherwise the call only has 0.2*timeout to get to the
			// receiver in the first place.
			ia.scheduleStep(step);
		}
	}
	
	/**
	 *  Automatically update the timer of a long running service call future.
	 *  Ensures that the caller does not timeout even if no result
	 *  value is set in that timespan.
	 *  The call periodically sends alive calls to the caller. 
	 *  @param ret The future that is returned by the service call.
	 *  @param ea The component handling the service call (on that component the periodic updates are scheduled).
	 *  @param to The timeout.
	 *  @param factor (default 0.8) Used to update the timer when factor*to has elapsed.
	 */
	public static void avoidCallTimeouts(final Future ret, IExternalAccess ea, final long to, final double factor, final boolean realtime)
	{
		if(to>0)
		{
			ea.scheduleStep(new ImmediateComponentStep()
			{
				public IFuture execute(IInternalAccess ia)
				{
					avoidCallTimeouts(ret, ia, to, factor, realtime);
					return IFuture.DONE;
				}
			});
		}
	}
	
	/**
	 *  Convenience method for creating a future (possibly with timeout avoidance).
	 *  @param timeouts (default is true) False, if no timeouts should be generated when service call timeout has elapsed.
	 *  @param ea The external access.
	 */
	public static  Future getNoTimeoutFuture(IInternalAccess ia)
	{
		return getFuture((Class)Future.class, false, ia);
	}
	
	/**
	 *  Convenience method for creating a future (possibly with timeout avoidance).
	 *  @param timeouts (default is true) False, if no timeouts should be generated when service call timeout has elapsed.
	 *  @param ia The internal access.
	 */
	public static  Future getNoTimeoutFuture(Class type, IInternalAccess ia)
	{
		return getFuture(type, false, ia);
	}
	
	/**
	 *  Convenience method for creating a future with timeout avoidance.
	 *  @param type The future type (e.g. IntermediateFuture.class).
	 *  @param ia The internal access.
	 *  @param realtime	true, for real time timeouts (simulation clock based timeouts otherwise).
	 */
	public static  Future getNoTimeoutFuture(Class type, IInternalAccess ia, boolean realtime)
	{
		Future ret = getFuture(type);
		avoidCallTimeouts(ret, ia, realtime);
		return ret;
	}
	
	/**
	 *  Convenience method for creating a future (possibly with timeout avoidance).
	 *  @param timeouts (default is true) False, if no timeouts should be generated when service call timeout has elapsed.
	 *  @param ea The external access.
	 */
	public static  Future getNoTimeoutFuture(Class type, IExternalAccess ea)
	{
		return getFuture(type, false, ea);
	}
	
	/**
	 *  Convenience method for creating a future (possibly with timeout avoidance).
	 *  @param timeouts (default is true) False, if no timeouts should be generated when service call timeout has elapsed.
	 *  @param ia The external access.
	 */
	public static  Future getFuture(boolean timeouts, IInternalAccess ia)
	{
		return getFuture((Class)Future.class, timeouts, ia);
	}
	
	/**
	 *  Convenience method for creating a future (possibly with timeout avoidance).
	 *  @param type The future implementation type.
	 *  @param timeouts (default is true) False, if no timeouts should be generated when service call timeout has elapsed.
	 *  @param ia The external access.
	 */
	public static  Future getFuture(Class type, boolean timeouts, IInternalAccess ia)
	{
		Future ret = getFuture(type);
			
		if(!timeouts)
			avoidCallTimeouts(ret, ia);
		
		return ret;
	}
	
	/**
	 *  Convenience method for creating a future (possibly with timeout avoidance).
	 *  @param type The future implementation type.
	 *  @param timeouts (default is true) False, if no timeouts should be generated when service call timeout has elapsed.
	 *  @param ea The external access.
	 */
	public static  Future getFuture(Class type, boolean timeouts, IExternalAccess ea)
	{
		Future ret = getFuture(type);
		
		if(!timeouts)
			avoidCallTimeouts(ret, ea);
		
		return ret;
	}
	
	/**
	 *  Get the matching future object to a future (interface) type.
	 */
	public static Future getFuture(Class clazz)
	{
		if(clazz==null)
			return new Future();
		
		Future ret = null;
		Exception ex	= null;
		
		if(!clazz.isInterface())
		{
			try
			{
				ret = (Future)clazz.newInstance();
			}
			catch(Exception e)
			{
				ex	= e;
			}
		}
		
		if(ret==null)
		{
			if(ITuple2Future.class.isAssignableFrom(clazz))
			{
				ret = new Tuple2Future();
			}
			else if(IPullSubscriptionIntermediateFuture.class.isAssignableFrom(clazz))
			{
				ret = new PullSubscriptionIntermediateDelegationFuture();
			}
			else if(IPullIntermediateFuture.class.isAssignableFrom(clazz))
			{
				ret = new PullIntermediateDelegationFuture();
			}
			else if(ISubscriptionIntermediateFuture.class.isAssignableFrom(clazz))
			{
				ret = new SubscriptionIntermediateDelegationFuture();
			}
			else if(ITerminableIntermediateFuture.class.isAssignableFrom(clazz))
			{
				ret = new TerminableIntermediateDelegationFuture();
			}
			else if(ITerminableFuture.class.isAssignableFrom(clazz))
			{
				ret = new TerminableDelegationFuture();
			}
			else if(IIntermediateFuture.class.isAssignableFrom(clazz))
			{
				ret	= new IntermediateFuture();
			}
			else if(IFuture.class.isAssignableFrom(clazz))
			{
				ret	= new Future();
			}
			else if(ex!=null)
			{
				throw SUtil.throwUnchecked(ex);
			}
			else
			{
				throw new RuntimeException("No future type: "+clazz);
			}
		}
		
		return ret;
	}

	/**
	 *  Blocking wait for first result.
	 *  Future is terminated after first result is received.
	 *  Defaults to realtime timeout (hack?)
	 *  @param fut	The future.
	 *  @return The first result.
	 */
	public static  T getFirstResultAndTerminate(ITerminableIntermediateFuture fut)
	{
		IComponentIdentifier	local	= IComponentIdentifier.LOCAL.get();
		T ret = fut.getNextIntermediateResult(Timeout.UNSET, local!=null ? Starter.isRealtimeTimeout(local, true) : true);
		fut.terminate();
		return ret;
	}
	
	/**
	 *  Combine results of two subscription futures and exclude duplicates 
	 *  (uses sliding cuckoo filter with toString() on results).
	 *  @param f1 Future 1.
	 *  @param f2 Future 2.
	 *  @return A future combining results of f1 and f2.
	 */
	public static  ISubscriptionIntermediateFuture combineSubscriptionFutures(IInternalAccess ia, ISubscriptionIntermediateFuture f1, ISubscriptionIntermediateFuture f2)
	{
		return combineSubscriptionFutures(ia, f1, f2, null);
	}
	
	/**
	 *  Combine results of two subscription futures and exclude duplicates 
	 *  (uses sliding cuckoo filter with toString() on results).
	 *  @param f1 Future 1.
	 *  @param f2 Future 2.
	 *  @return A future combining results of f1 and f2.
	 */
	public static  ISubscriptionIntermediateFuture combineSubscriptionFutures(IInternalAccess ia, ISubscriptionIntermediateFuture f1, ISubscriptionIntermediateFuture f2, IResultCommand cmd)
	{
		final SlidingCuckooFilter scf = new SlidingCuckooFilter();
	
		ISubscriptionIntermediateFuture ret = (ISubscriptionIntermediateFuture)FutureFunctionality
			.getDelegationFuture(f1, new ComponentFutureFunctionality(ia)
		{
			@Override
			public Object handleIntermediateResult(Object result) throws Exception
			{
				// Drop result when already in cuckoo filter
				
				if(result instanceof ServiceEvent)
				{
					ServiceEvent se = (ServiceEvent)result;
					
					if(ServiceEvent.SERVICE_REMOVED == se.getType())
					{
						return removeValue(result);
					}
					else if(ServiceEvent.SERVICE_ADDED == se.getType())
					{
						return addValue(result);
					}
					return DROP_INTERMEDIATE_RESULT;
				}
				// In case of no service events always new elements are reported
				else
				{
					return addValue(result);
				}
			}
			
			protected Object addValue(Object val)
			{
				if(scf.contains(val.toString()))
				{
					return DROP_INTERMEDIATE_RESULT;
				}
				else
				{
					// todo: allow transforming the results?!
					scf.insert(val.toString());
					T res = cmd!=null? cmd.execute((E)val): (T)val;
					return res;
				}
			}
			
			protected Object removeValue(Object val)
			{
				if(scf.contains(val.toString()))
				{
					scf.delete(val.toString());
					T res = cmd!=null? cmd.execute((E)val): (T)val;
					return res;
				}
				else
				{
					return DROP_INTERMEDIATE_RESULT;
				}
			}
			
			@Override
			public void handleTerminated(Exception reason)
			{
				// TODO: multi delegation future with multiple sources but one target?
				if(f2!=null)
					f2.terminate(reason);
				
				super.handleTerminated(reason);
			}
		});
		
		SFuture.avoidCallTimeouts((IntermediateFuture)ret, ia);
		
		// Add remote results to future
		if(f2!=null)
		{
			f2.next(result-> 
			{
				((IntermediateFuture)ret).addIntermediateResult((T)result);
			}).catchEx(exception -> {}); // Ignore exception (printed when no listener supplied)
		}
		
		return ret;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy