Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
jadex.requiredservice.impl.MicroRequiredServiceFeature Maven / Gradle / Ivy
package jadex.requiredservice.impl;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jadex.bytecode.ProxyFactory;
import jadex.common.IParameterGuesser;
import jadex.common.SAccess;
import jadex.common.SReflect;
import jadex.common.SUtil;
import jadex.common.Tuple2;
import jadex.core.impl.Component;
import jadex.execution.IExecutionFeature;
import jadex.future.CounterResultListener;
import jadex.future.DelegationResultListener;
import jadex.future.Future;
import jadex.future.IFuture;
import jadex.future.IResultListener;
import jadex.future.ISubscriptionIntermediateFuture;
import jadex.future.IntermediateEmptyResultListener;
import jadex.javaparser.SJavaParser;
import jadex.micro.MicroAgent;
import jadex.model.IModelFeature;
import jadex.model.impl.AbstractModelLoader;
import jadex.model.modelinfo.ModelInfo;
import jadex.providedservice.IService;
import jadex.providedservice.IServiceIdentifier;
import jadex.providedservice.ServiceScope;
import jadex.providedservice.impl.search.ServiceEvent;
import jadex.providedservice.impl.search.ServiceNotFoundException;
import jadex.providedservice.impl.search.ServiceQuery;
import jadex.requiredservice.IRequiredServiceFeature;
import jadex.requiredservice.RequiredServiceInfo;
public class MicroRequiredServiceFeature extends RequiredServiceFeature
{
protected MicroRequiredServiceFeature(Component self)
{
super(self);
}
@Override
public void onStart()
{
super.onStart();
ModelInfo model = self.hasFeature(IModelFeature.class)? (ModelInfo)self.getFeature(IModelFeature.class).getModel(): null;
RequiredServiceModel mymodel = model!=null? (RequiredServiceModel)model.getFeatureModel(IRequiredServiceFeature.class): null;
if(mymodel!=null)
{
final RequiredServiceModel fmymodel = mymodel;
String[] sernames = mymodel.getServiceInjectionNames();
Stream> s = Arrays.stream(sernames).map(sername -> new Tuple2(sername, fmymodel.getServiceInjections(sername)));
Map serinfos = s.collect(Collectors.toMap(t -> t.getFirstEntity(), t -> t.getSecondEntity()));
Object pojo = ((MicroAgent)self).getPojo(); // hack
injectServices(getComponent(), pojo, sernames, serinfos, mymodel).get();
}
}
public RequiredServiceModel loadModel()
{
ModelInfo model = (ModelInfo)self.getFeature(IModelFeature.class).getModel();
RequiredServiceModel mymodel = (RequiredServiceModel)model.getFeatureModel(IRequiredServiceFeature.class);
if(mymodel==null)
{
mymodel = (RequiredServiceModel)MicroRequiredServiceLoader.readFeatureModel(((MicroAgent)self).getPojo().getClass(), this.getClass().getClassLoader());
final RequiredServiceModel fmymodel = mymodel;
AbstractModelLoader loader = AbstractModelLoader.getLoader((Class< ? extends Component>)self.getClass());
loader.updateCachedModel(() ->
{
model.putFeatureModel(IRequiredServiceFeature.class, fmymodel);
});
}
return mymodel;
}
/**
* Inject the services and initialize queries.
*/
public static IFuture injectServices(Component component, Object target, String[] sernames, Map serinfos, RequiredServiceModel rsm)
{
final Future ret = new Future();
// Inject required services
// Fetch all injection names - field and method injections
//String[] sernames = model.getServiceInjectionNames();
if(sernames.length>0)
{
CounterResultListener lis = new CounterResultListener(sernames.length,
new DelegationResultListener(ret));
for(int i=0; i lis2 = new CounterResultListener(infos.length, lis);
String sername = (String)SJavaParser.evaluateExpressionPotentially(sernames[i], component.getFeature(IModelFeature.class).getModel().getAllImports(), component.getFeature(IModelFeature.class).getFetcher(), component.getClassLoader());
//if(sername!=null && sername.indexOf("calc")!=-1)
// System.out.println("calc");
for(int j=0; j query = createServiceQuery(component, info);
// if query
if(infos[j].getQuery()!=null && infos[j].getQuery().booleanValue())
{
//ServiceQuery query = new ServiceQuery<>((Class)info.getType().getType(component.getClassLoader()), info.getDefaultBinding().getScope());
//query = info.getTags()==null || info.getTags().size()==0? query: query.setServiceTags(info.getTags().toArray(new String[info.getTags().size()]), component.getExternalAccess());
// Set event mode to get also removed events
query.setEventMode();
long to = infos[j].getActive();
ISubscriptionIntermediateFuture sfut = to>0?
component.getFeature(IRequiredServiceFeature.class).addQuery(query, to):
component.getFeature(IRequiredServiceFeature.class).addQuery(query);
// Directly continue with init when service is not required
if(infos[j].getRequired()==null || !infos[j].getRequired().booleanValue())
lis2.resultAvailable(null);
final int fj = j;
// Invokes methods for each intermediate result
sfut.addResultListener(new IntermediateEmptyResultListener()
{
boolean first = true;
public void intermediateResultAvailable(final Object result)
{
//System.out.println("agent received service event: "+result);
/*if(result==null)
{
System.out.println("received null as service: "+infos[fj]);
return;
}*/
// todo: multiple parameters and using parameter annotations?!
// todo: multiple parameters and wait until all are filled?!
if(infos[fj].getMethodInfo()!=null)
{
Method m = SReflect.getAnyMethod(target.getClass(), infos[fj].getMethodInfo().getName(), infos[fj].getMethodInfo().getParameterTypes(component.getClassLoader()));
invokeMethod(m, target, result, component);
}
else if(infos[fj].getFieldInfo()!=null)
{
final Field f = infos[fj].getFieldInfo().getField(component.getClassLoader());
setDirectFieldValue(f, target, result, component);
}
// Continue with agent init when first service is found
if(first)
{
first = false;
if(infos[fj].getRequired()==null || infos[fj].getRequired().booleanValue())
lis2.resultAvailable(null);
}
}
public void resultAvailable(Collection result)
{
finished();
}
public void exceptionOccurred(Exception e)
{
// todo:
// if(!(e instanceof ServiceNotFoundException)
// || m.getAnnotation(AgentServiceSearch.class).required())
// {
// component.getLogger().warning("Method injection failed: "+e);
// }
// else
{
// Call self with empty list as result.
finished();
}
}
});
}
// if is search
else
{
if(infos[j].getFieldInfo()!=null)
{
final Field f = infos[j].getFieldInfo().getField(component.getClassLoader());
Class> ft = f.getDeclaringClass();
boolean multiple = ft.isArray() || SReflect.isSupertype(Collection.class, ft) || info.getMax()>2;
final IFuture sfut = callgetService(sername, info, component, multiple);
// todo: what about multi case?
// why not add values to a collection as they come?!
// currently waits until the search has finished before injecting
// Is annotation is at field and field is of type future directly set it
if(SReflect.isSupertype(IFuture.class, f.getType()))
{
try
{
SAccess.setAccessible(f, true);
f.set(target, sfut);
lis2.resultAvailable(null);
}
catch(Exception e)
{
System.out.println("Field injection failed: "+e);
lis2.exceptionOccurred(e);
}
}
else
{
// if future is already done
if(sfut.isDone() && sfut.getException() == null)
{
try
{
setDirectFieldValue(f, target, sfut.get(), component);
lis2.resultAvailable(null);
}
catch(Exception e)
{
lis2.exceptionOccurred(e);
}
}
else if(infos[j].getLazy()!=null && infos[j].getLazy().booleanValue() && !multiple)
{
//RequiredServiceInfo rsi = ((IInternalRequiredServicesFeature)component.getFeature(IRequiredServicesFeature.class)).getServiceInfo(sername);
Class> clz = info.getType().getType(component.getClassLoader(), component.getFeature(IModelFeature.class).getModel().getAllImports());
//ServiceQuery query = RequiredServicesComponentFeature.getServiceQuery(component, info);
UnresolvedServiceInvocationHandler h = new UnresolvedServiceInvocationHandler(component, query);
Object proxy = ProxyFactory.newProxyInstance(component.getClassLoader(), new Class[]{IService.class, clz}, h);
try
{
SAccess.setAccessible(f, true);
f.set(target, proxy);
lis2.resultAvailable(null);
}
catch(Exception e)
{
System.out.println("Field injection failed: "+e);
lis2.exceptionOccurred(e);
}
}
else
{
// todo: remove!
// todo: disallow multiple field injections!
// This is problematic because search can defer the agent startup esp. when remote search
// Wait for result and block init until available
// Dangerous because agent blocks
final int fj = j;
sfut.addResultListener(new IResultListener()
{
public void resultAvailable(Object result)
{
try
{
setDirectFieldValue(f, target, result, component);
lis2.resultAvailable(null);
}
catch(Exception e)
{
lis2.exceptionOccurred(e);
}
}
public void exceptionOccurred(Exception e)
{
if(!(e instanceof ServiceNotFoundException)
|| (infos[fj].getRequired()!=null && infos[fj].getRequired().booleanValue()))
{
System.out.println("Field injection failed: "+e);
lis2.exceptionOccurred(e);
}
else
{
// Set empty list, set on exception
if(SReflect.isSupertype(f.getType(), List.class))
{
// Call self with empty list as result.
resultAvailable(new ArrayList());
}
else if(SReflect.isSupertype(f.getType(), Set.class))
{
// Call self with empty list as result.
resultAvailable(new HashSet());
}
else
{
// Don't set any value.
lis2.resultAvailable(null);
}
}
}
});
}
}
}
else if(infos[j].getMethodInfo()!=null)
{
// injection of future as parameter not considered meanigful case
// injection of lazy proxy not considered as meaningful case
final Method m = SReflect.getAnyMethod(target.getClass(), infos[j].getMethodInfo().getName(), infos[j].getMethodInfo().getParameterTypes(component.getClassLoader()));
boolean multiple = info.getMax()>2;
final IFuture sfut = callgetService(sername, info, component, multiple);
// if future is already done
if(sfut.isDone() && sfut.getException() == null)
{
try
{
invokeMethod(m, target, sfut.get(), component);
lis2.resultAvailable(null);
}
catch(Exception e)
{
lis2.exceptionOccurred(e);
}
}
else
{
sfut.addResultListener(new IResultListener()
{
@Override
public void resultAvailable(Object result)
{
try
{
invokeMethod(m, target, sfut.get(), component);
lis2.resultAvailable(null);
}
catch(Exception e)
{
lis2.exceptionOccurred(e);
}
}
@Override
public void exceptionOccurred(Exception exception)
{
lis2.exceptionOccurred(exception);
}
});
}
}
}
}
}
}
else
{
ret.setResult(null);
}
return ret;
}
/**
*
*/
protected static void setDirectFieldValue(Field f, Object target, Object result, Component component)
{
//boolean multiple = ft.isArray() || SReflect.isSupertype(Collection.class, ft) || info.getMax()>2;
//System.out.println("setDirectFieldValue: "+result+" "+component.getId());
ServiceEvent event = result instanceof ServiceEvent? (ServiceEvent)result: null;
if(event!=null)
{
IServiceIdentifier sid = event.getService();
IService result2 = getServiceProxy(sid, null, component);
if(event.getType()==ServiceEvent.SERVICE_ADDED)
{
if(!addDirectFieldValue(f, target, result))
{
if(!addDirectFieldValue(f, target, result2))
{
System.out.println("could not add value: "+result);
//throw new RuntimeException("Could not add/set service value: "+result);
}
}
}
else if(event.getType()==ServiceEvent.SERVICE_REMOVED)
{
if(!removeDirectFieldValue(f, target, result))
{
if(!removeDirectFieldValue(f, target, result2))
{
System.out.println("could not remove value: "+result);
//throw new RuntimeException("Could not remove service value: "+result);
}
}
}
}
else
{
// default is set value
addDirectFieldValue(f, target, result);
}
}
protected static boolean addDirectFieldValue(Field f, Object target, Object result)
{
boolean ret = false;
Class> ft = f.getType();
SAccess.setAccessible(f, true);
try
{
if(SReflect.isSupertype(ft, result.getClass()))
{
try
{
f.set(target, result);
ret = true;
}
catch(Throwable t)
{
throw SUtil.throwUnchecked(t);
}
}
else if(ft.isArray())
{
// find next null value and insert new value there
Class> ct = ft.getComponentType();
if(SReflect.isSupertype(ct, result.getClass()))
{
try
{
Object ar = f.get(target);
for(int i=0; i type = SReflect.getIterableComponentType(f.getGenericType());
if(SReflect.isSupertype(type, result.getClass()))
{
List coll = (List)f.get(target);
if(coll==null)
{
coll = new ArrayList();
try
{
f.set(target, coll);
}
catch(Exception e)
{
throw SUtil.throwUnchecked(e);
}
}
coll.add(result);
ret = true;
}
}
catch(Exception e)
{
//throw SUtil.throwUnchecked(e);
}
}
else if(SReflect.isSupertype(Set.class, ft))
{
try
{
Class> type = SReflect.getIterableComponentType(f.getGenericType());
if(SReflect.isSupertype(type, result.getClass()))
{
Set coll = (Set)f.get(target);
if(coll==null)
{
coll = new HashSet();
try
{
f.set(target, coll);
}
catch(Exception e)
{
throw SUtil.throwUnchecked(e);
}
}
coll.add(result);
ret = true;
}
}
catch(Exception e)
{
throw SUtil.throwUnchecked(e);
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
return ret;
}
/**
* Call
* @param sername
* @param info
* @return
*/
protected static IFuture callgetService(String sername, RequiredServiceInfo info, Component component, boolean multiple)
{
final IFuture sfut;
// if info is available use it. in case of services it is not available in the agent (model)
if(info!=null)
{
if(multiple)
{
IFuture ifut = component.getFeature(IRequiredServiceFeature.class).searchServices(createServiceQuery(component, info));
sfut = ifut;
}
else
{
IFuture ifut = component.getFeature(IRequiredServiceFeature.class).searchService(createServiceQuery(component, info));
sfut = ifut;
}
}
else
{
if(multiple)
{
IFuture ifut = component.getFeature(IRequiredServiceFeature.class).getServices(sername);
sfut = ifut;
}
else
{
IFuture ifut = component.getFeature(IRequiredServiceFeature.class).getService(sername);
sfut = ifut;
}
}
return sfut;
}
/**
* When searching for declared service -> map required service declaration to service query.
*/
public static ServiceQuery createServiceQuery(Component component, RequiredServiceInfo info)
{
// Evaluate and replace scope expression, if any.
ServiceScope scope = info.getDefaultBinding()!=null ? info.getDefaultBinding().getScope() : null;
/*if(ServiceScope.EXPRESSION.equals(scope))
{
scope = (ServiceScope)SJavaParser.getParsedValue(info.getDefaultBinding().getScopeExpression(), component.getFeature(IModelFeature.class).getModel().getAllImports(), component.getFeature(IModelFeature.class).getFetcher(), component.getClassLoader());
info = new RequiredServiceInfo(info.getName(), info.getType(), info.getMin(), info.getMax(),
new RequiredServiceBinding(info.getDefaultBinding()).setScope(scope),
//info.getNFRProperties(),
info.getTags());
}*/
return getServiceQuery(component, info);
}
/**
*
* @param m
* @param target
* @param result
*/
protected static void invokeMethod(Method m, Object target, Object result, Component component)
{
Object[] args = new Object[m.getParameterCount()];
IParameterGuesser guesser = null;
if(component.getFeature(IModelFeature.class)!=null)
guesser = component.getFeature(IModelFeature.class).getParameterGuesser();
boolean invoke = fillMethodParameter(m, args, result, guesser);
if(!invoke && result instanceof ServiceEvent)
{
ServiceEvent event = (ServiceEvent)result;
IServiceIdentifier sid = event.getService();
result = getServiceProxy(sid, null, component);
if(event.getType()==ServiceEvent.SERVICE_ADDED)
{
invoke = fillMethodParameter(m, args, result, guesser);
}
else if(event.getType()==ServiceEvent.SERVICE_REMOVED)
{
// do not invoke @OnService with removed service?! Do we want @OnServiceRemoved or @OnService(type=removed)
}
}
if(invoke)
{
// what to do with exception in user code?
component.getFeature(IExecutionFeature.class).scheduleStep(() ->
{
try
{
SAccess.setAccessible(m, true);
m.invoke(target, args);
}
catch(Exception t)
{
//throw SUtil.throwUnchecked(t);
t.printStackTrace();
}
});
}
/*else
{
System.out.println("cannot invoke method as result type does not fit parameter types: "+result+" "+m);
}*/
}
protected static boolean fillMethodParameter(Method m, Object[] args, Object result, IParameterGuesser guesser)
{
boolean ret = false;
for(int i=0; i ft = f.getType();
SAccess.setAccessible(f, true);
try
{
if(SReflect.isSupertype(ft, result.getClass()))
{
try
{
f.set(target, null);
ret = true;
}
catch(Throwable t)
{
throw SUtil.throwUnchecked(t);
}
}
else if(ft.isArray())
{
// find next null value and insert new value there
Class> ct = ft.getComponentType();
if(SReflect.isSupertype(ct, result.getClass()))
{
try
{
Object ar = f.get(target);
for(int i=0; i coll = (List)f.get(target);
if(coll!=null && coll.contains(result))
{
coll.remove(result);
ret = true;
}
}
catch(Exception e)
{
//throw SUtil.throwUnchecked(e);
}
}
else if(SReflect.isSupertype(Set.class, ft))
{
try
{
Set coll = (Set)f.get(target);
if(coll!=null && coll.contains(result))
{
coll.remove(result);
ret = true;
//System.out.println("removed: "+coll.size());
}
}
catch(Exception e)
{
//throw SUtil.throwUnchecked(e);
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
return ret;
}
}