jadex.bdi.model.BDIClassReader Maven / Gradle / Ivy
The newest version!
package jadex.bdi.model;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jadex.bdi.annotation.Belief;
import jadex.bdi.annotation.Body;
import jadex.bdi.annotation.Capability;
import jadex.bdi.annotation.Deliberation;
import jadex.bdi.annotation.ExcludeMode;
import jadex.bdi.annotation.Goal;
import jadex.bdi.annotation.GoalContextCondition;
import jadex.bdi.annotation.GoalCreationCondition;
import jadex.bdi.annotation.GoalDropCondition;
import jadex.bdi.annotation.GoalInhibit;
import jadex.bdi.annotation.GoalMaintainCondition;
import jadex.bdi.annotation.GoalParameter;
import jadex.bdi.annotation.GoalRecurCondition;
import jadex.bdi.annotation.GoalServiceParameterMapping;
import jadex.bdi.annotation.GoalServiceResultMapping;
import jadex.bdi.annotation.GoalTargetCondition;
import jadex.bdi.annotation.Goals;
import jadex.bdi.annotation.Mapping;
import jadex.bdi.annotation.Plan;
import jadex.bdi.annotation.PlanContextCondition;
import jadex.bdi.annotation.Plans;
import jadex.bdi.annotation.RawEvent;
import jadex.bdi.annotation.ServicePlan;
import jadex.bdi.annotation.ServiceTrigger;
import jadex.bdi.annotation.Trigger;
import jadex.bdi.runtime.ChangeEvent;
import jadex.bdi.runtime.impl.BDIAgentFeature;
import jadex.bdi.runtime.impl.IServiceParameterMapper;
import jadex.classreader.SClassReader;
import jadex.common.ClassInfo;
import jadex.common.FieldInfo;
import jadex.common.MethodInfo;
import jadex.common.SReflect;
import jadex.common.SUtil;
import jadex.common.Tuple2;
import jadex.micro.MicroClassReader;
import jadex.micro.MicroModel;
import jadex.micro.annotation.Agent;
import jadex.model.modelinfo.ModelInfo;
import jadex.rules.eca.EventType;
/**
* Reads micro agent classes and generates a model from metainfo and annotations.
*/
public class BDIClassReader extends MicroClassReader
{
/** The class generator. */
protected IBDIClassGenerator gen;
/** The model loader for subcapabilities. */
protected BDIModelLoader loader;
/**
* Create a new bdi class reader.
*/
public BDIClassReader(BDIModelLoader loader)
{
this.gen = BDIClassGeneratorFactory.getInstance().createBDIClassGenerator();
this.loader = loader;
}
/**
* Set the generator.
* @param gen the gen to set
*/
public void setGenerator(IBDIClassGenerator gen)
{
this.gen = gen;
}
/**
* Load a model.
* @param model The model (e.g. file name).
* @param imports (if any).
* @return The loaded model.
*/
@Override
public MicroModel read(String model, Object pojo, String[] imports, ClassLoader classloader)//, IComponentIdentifier root)//
{
// use dummy classloader that will not be visisble outside
if(pojo==null)
{
List urls = SUtil.getClasspathURLs(classloader, false);
classloader = createDummyClassLoader(classloader, null, urls);
}
return super.read(model, pojo, imports, classloader);
}
/**
* Create a throw away class loader.
*/
protected DummyClassLoader createDummyClassLoader(ClassLoader original, ClassLoader parent, List urls)
{
return new DummyClassLoader((URL[])urls.toArray(new URL[urls.size()]), parent, original);
}
/**
* Load the model.
*/
@Override
protected MicroModel read(String model, Class> cma, ClassLoader cl)//, IComponentIdentifier root)
{
// can be original when pojo is used
ClassLoader classloader = cl instanceof DummyClassLoader? ((DummyClassLoader)cl).getOriginal(): cl;
ModelInfo modelinfo = new ModelInfo();
BDIModel ret = new BDIModel(modelinfo, new MCapability(cma.getName()));
modelinfo.internalSetRawModel(ret);
ret.setPojoClass(new ClassInfo(cma.getName()));
String name = SReflect.getUnqualifiedClassName(cma);
// if(name.endsWith(BDIModelLoader.FILE_EXTENSION_BDIV3_FIRST))
// name = name.substring(0, name.lastIndexOf(BDIModelLoader.FILE_EXTENSION_BDIV3_FIRST));
String packagename = cma.getPackage()!=null? cma.getPackage().getName(): null;
// modelinfo.setName(name+"BDI");
modelinfo.setName(name);
modelinfo.setPackage(packagename);
modelinfo.setFilename(SUtil.getClassFileLocation(cma));
// modelinfo.setStartable(!Modifier.isAbstract(cma.getModifiers()));
// modelinfo.setStartable(cma.getName().endsWith(BDIModelLoader.FILE_EXTENSION_BDIV3_FIRST));
modelinfo.setType("bdi");
// modelinfo.setClassloader(classloader);
// ret.setClassloader(classloader); // use parent
// System.out.println("filename: "+modelinfo.getFilename());
fillMicroModelFromAnnotations(ret, model, cma, cl);
fillBDIModelFromAnnotations(ret, model, cma, cl);
// why do we need to check on generated class the modifiers?
// Class> genclass = SReflect.findClass0(cma.getName(), null, classloader);
// if(genclass!=null)
// modelinfo.setStartable(!Modifier.isAbstract(genclass.getModifiers()));
// else // can happen when no class is generated (eg. pojo case)
// modelinfo.setStartable(!Modifier.isAbstract(cma.getModifiers()));
initBDIModelAfterClassLoading(ret, classloader);
return ret;
}
/**
* Fill the model details using annotation.
* // called with dummy classloader (that was used to load cma first time)
*/
protected void fillBDIModelFromAnnotations(BDIModel bdimodel, String model, Class> cma, ClassLoader cl)
{
// ModelInfo modelinfo = (ModelInfo)micromodel.getModelInfo();
// System.out.println("todo: read bdi");
// List s = new ArrayList();
// final Set beliefnames = new HashSet();
// List goals = new ArrayList();
// List plans = new ArrayList();
// try
// {
final Class> fcma = cma;
Map capas = new LinkedHashMap();
Map>> pubs = new HashMap>>();
List> agtcls = new ArrayList>();
while(cma!=null && !cma.equals(Object.class))// && !cma.equals(getClass(BDIAgent.class, cl)))
{
if(isAnnotationPresent(cma, Agent.class, cl)
|| isAnnotationPresent(cma, Capability.class, cl))
{
agtcls.add(0, cma);
}
cma = cma.getSuperclass();
}
for(Class> clazz: agtcls)
{
Field[] fields = clazz.getDeclaredFields();
// Find capabilities
for(int i=0; i outer concrete belief)
}
// Remember field of inner capability (e.g. agentpojo.mycapa)
bdimodel.addSubcapability(new FieldInfo(fields[i]), cap);
// Copy subcapability fields of inner capability (e.g. agentpojo.mycapa.mysubcapa)
if(cap.getSubcapabilities()!=null)
{
for(Tuple2 sub: cap.getSubcapabilities())
{
// Use nested field info
bdimodel.addSubcapability(new FieldInfo(fields[i], sub.getFirstEntity()), sub.getSecondEntity());
}
}
}
catch(Exception e)
{
throw SUtil.throwUnchecked(e);
}
}
}
}
// Merge capabilities into flat agent model
SBDIModel.mergeSubcapabilities(bdimodel, capas, cl);
for(Class> clazz: agtcls)
{
// Find beliefs
Field[] fields = clazz.getDeclaredFields();
for(int i=0; i rawevents = null;
if(bel.rawevents().length>0)
{
rawevents = new HashSet();
RawEvent[] rawevs = bel.rawevents();
for(RawEvent rawev: rawevs)
{
rawevents.add(BDIAgentFeature.createEventType(rawev));
}
}
boolean dynamic = bel.dynamic() || bel.updaterate()>0;// || bel.beliefs().length>0 || bel.rawevents().length>0;
bdimodel.getCapability().addBelief(new MBelief(new FieldInfo(fields[i]),
dynamic, bel.updaterate(), bel.beliefs().length==0? null: bel.beliefs(), rawevents));
// beliefs.add(fields[i]);
// beliefnames.add(fields[i].getName());
}
}
Method[] methods = clazz.getDeclaredMethods();
List lmethods = Arrays.asList(methods);
lmethods.sort((m1, m2) -> m1.getName().compareTo(m2.getName()));
methods = lmethods.toArray(new Method[methods.length]);
// Find method beliefs
for(int i=0; i rawevents = null;
if(bel.rawevents().length>0)
{
rawevents = new HashSet();
RawEvent[] rawevs = bel.rawevents();
for(RawEvent rawev: rawevs)
{
rawevents.add(BDIAgentFeature.createEventType(rawev));
}
}
boolean dynamic = bel.dynamic() || bel.updaterate()>0;// || rawevents!=null || bel.beliefs().length>0;
bdimodel.getCapability().addBelief(new MBelief(new MethodInfo(methods[i]),
dynamic, bel.updaterate(), bel.beliefs().length==0? null: bel.beliefs(), rawevents));
}
}
}
// Find external goals
if(isAnnotationPresent(clazz, Goals.class, cl))
{
Goal[] goals = getAnnotation(clazz, Goals.class, cl).value();
for(Goal goal: goals)
{
getMGoal(bdimodel, goal, goal.clazz(), cl, pubs);
}
}
// Find inner goal and plan classes
Class>[] cls = clazz.getDeclaredClasses();
List> lcls = Arrays.asList(cls);
lcls.sort((c1, c2) -> c1.getName().compareTo(c2.getName()));
cls = lcls.toArray(new Class[cls.length]);
for(int i=0; i mgoals = bdimodel.getCapability().getGoals();
// for(MGoal mgoal: mgoals)
// {
// MDeliberation delib = mgoal.getDeliberation();
// if(delib!=null)
// {
// delib.init(bdimodel.getCapability());
// }
// }
// ModelInfo modelinfo = (ModelInfo)bdimodel.getModelInfo();
// if(confs.size()>0)
// {
// modelinfo.setConfigurations((ConfigurationInfo[])confs.values().toArray(new ConfigurationInfo[confs.size()]));
// bdimodel.getCapability().setConfigurations(bdiconfs);
// }
// Evaluate the published goals and create provided services for them
for(Iterator it = pubs.keySet().iterator(); it.hasNext(); )
{
ClassInfo key = it.next();
List> vals = pubs.get(key);
Map goalnames = new LinkedHashMap();
for(Tuple2 val: vals)
{
goalnames.put(val.getSecondEntity(), val.getFirstEntity().getName());
}
// System.out.println("found goal publish: "+key);
StringBuffer buf = new StringBuffer();
buf.append("jadex.bdi.runtime.impl.GoalDelegationHandler.createServiceImplementation($component, ");
buf.append(key.getTypeName()+".class, ");
buf.append("new String[]{");
for(Iterator it2=goalnames.keySet().iterator(); it2.hasNext(); )
{
buf.append("\"").append(it2.next()).append("\"");
if(it2.hasNext())
buf.append(", ");
}
buf.append("}, ");
buf.append("new String[]{");
for(Iterator it2=goalnames.keySet().iterator(); it2.hasNext(); )
{
buf.append("\"").append(goalnames.get(it2.next())).append("\"");
if(it2.hasNext())
buf.append(", ");
}
buf.append("}");
buf.append(")");
// System.out.println("service creation expression: "+buf.toString());
// ProvidedServiceImplementation psi = new ProvidedServiceImplementation(null, buf.toString(),
// BasicServiceInvocationHandler.PROXYTYPE_DECOUPLED, null, null);
//
// // todo: allow specifying scope
// modelinfo.addProvidedService(new ProvidedServiceInfo(null, key, psi));
}
// Create enhanced classes if not already present.
if(cl instanceof DummyClassLoader) // else is pojo case without generation
{
for(Class> agcl: agtcls)
{
try
{
if(!IBDIClassGenerator.isPure(agcl) && !IBDIClassGenerator.isEnhanced(agcl))
gen.generateBDIClass(agcl.getName(), bdimodel, cl);
// else
// System.out.println("already enhanced: "+agcl);
}
catch (JadexBDIGenerationException e)
{
throw new JadexBDIGenerationRuntimeException("Could not read bdi agent: " + agcl, e);
}
// System.out.println("genclazz: "+agcl.getName()+" "+agcl.hashCode()+" "+agcl.getClassLoader());
}
// Sort the plans according to their declaration order in the source file
// Must be done after class enhancement to contain the "__getLineNumber()" method
ClassLoader classloader = ((DummyClassLoader)cl).getOriginal();
// HacK?!
// todo: how to handle order of inner plan classes?
// possible solution would be using asm to generate a line number map for the plans :-(
SClassReader.ClassInfo ci = SClassReader.getClassInfo(fcma.getName(), cl, true, true);
// can null when agent class is not available on disk (pure pojo agent)
if(ci!=null)
{
//System.out.println("methods of "+fcma+" "+ci.getMethodInfos());
Map order = new HashMap<>();
int cnt = 0;
for(SClassReader.MethodInfo mi: SUtil.notNull(ci.getMethodInfos()))
{
order.put(mi.getMethodName(), cnt++);
}
bdimodel.getCapability().sortPlans(order, classloader);
}
}
// System.out.println("genclazz: "+genclazz);
// System.out.println("endend");
// }
// catch(Exception e)
// {
// e.printStackTrace();
// }
}
/**
*
*/
protected MTrigger buildPlanTrigger(BDIModel bdimodel, String name, Trigger trigger, ClassLoader cl, Map>> pubs)
{
MTrigger tr = null;
Class>[] gs = trigger.goals();
Class>[] gfs = trigger.goalfinisheds();
ServiceTrigger st = trigger.service();
if(gs.length>0 || gfs.length>0
|| trigger.factadded().length>0 || trigger.factremoved().length>0 || trigger.factchanged().length>0
|| st.name().length()>0 || !Object.class.equals(st.type()))
{
tr = new MTrigger();
for(int j=0; j0? st.name(): null;
Class> sty = !Object.class.equals(st.type())? st.type(): null;
String mn = st.method().length()>0? st.method(): null;
Class> stype = sty;
if(sty==null && sn!=null)
{
// ProvidedServiceInfo[] provs = bdimodel.getModelInfo().getProvidedServices();
// for(ProvidedServiceInfo prov: provs)
// {
// if(prov.getName().equals(sn))
// {
// stype = prov.getType().getType(bdimodel.getClassloader(), bdimodel.getModelInfo().getAllImports());
// break;
// }
// }
}
Method m = null;
if(stype!=null)
{
if(mn!=null)
{
Method[] ms = SReflect.getMethods(stype, mn);
if(ms.length>0)
{
m = ms[0];
}
}
else
{
Method[] ms = stype.getDeclaredMethods();
if(ms.length>0)
{
m = ms[0];
}
}
}
MServiceCall ret = null;
if(m!=null)
{
ret = bdimodel.getCapability().getService(m.toString());
if(ret==null)
{
ret = new MServiceCall(m.toString(), false, false, ExcludeMode.WhenTried);
bdimodel.getCapability().addservice(ret);
}
}
return ret;
}
/**
*
*/
protected MPlan getMPlan(BDIModel bdimodel, Plan p, MethodInfo mi, ClassInfo ci,
ClassLoader cl, Map>> pubs, int order)
{
String name = null;
Body body = p.body();
ServicePlan sp = body.service();
String component = body.component();
// Generate the plan name:
if(mi!=null)
{
// Method name if plan is method
name = mi.getName();
}
else if(ci!=null)
{
// Class name if is class
name = ci.getTypeName();
}
else if(!Object.class.equals(body.value()))
{
// Class name if is class
// name = SReflect.getInnerClassName(body.value());
name = body.value().getName();
}
else if(sp.name().length()>0)
{
// Service plan name if is service
name = sp.name()+"_"+sp.method();
}
else if(component.length()>0)
{
// Plan is subcomponent
name = component;
if(name.indexOf("/")!=-1)
{
name = name.substring(name.lastIndexOf("/")+1);
}
if(name.indexOf(".")!=-1)
{
name = name.substring(0, name.lastIndexOf("."));
}
}
else
{
throw new RuntimeException("Plan body not found: "+p);
}
MPlan mplan = bdimodel.getCapability().getPlan(name);
if(mplan==null)
{
mplan = createMPlan(bdimodel, p, mi, name, ci, cl, pubs, order);
bdimodel.getCapability().addPlan(mplan);
}
return mplan;
}
/**
* Create a plan model.
*/
protected MPlan createMPlan(BDIModel bdimodel, Plan p, MethodInfo mi, String name,
ClassInfo ci, ClassLoader cl, Map>> pubs, int order)
{
Body body = p.body();
ServicePlan sp = body.service();
MPlan mplan = bdimodel.getCapability().getPlan(name);
if(mplan==null)
{
MTrigger mtr = buildPlanTrigger(bdimodel, name, p.trigger(), cl, pubs);
MTrigger wmtr = buildPlanTrigger(bdimodel, name, p.waitqueue(), cl, pubs);
// Check if external plan has a trigger defined
if(mtr==null && !Object.class.equals(body.value()))
{
Class> bcl = body.value();
Plan pl = getAnnotation(bcl, Plan.class, cl);
mtr = buildPlanTrigger(bdimodel, name, pl.trigger(), cl, pubs);
if(wmtr==null)
{
wmtr = buildPlanTrigger(bdimodel, name, pl.waitqueue(), cl, pubs);
}
}
if(ci==null)
ci = Object.class.equals(body.value())? null: new ClassInfo(body.value().getName());
// Class extends IServiceParameterMapper