bdi4jade
Changes
bdi-jade/src/bdi4jade/core/BDIAgent.java 47(+44 -3)
bdi-jade/src/bdi4jade/core/Capability.java 94(+93 -1)
bdi-jade/src/bdi4jade/core/Intention.java 31(+27 -4)
Details
bdi-jade/src/bdi4jade/core/BDIAgent.java 47(+44 -3)
diff --git a/bdi-jade/src/bdi4jade/core/BDIAgent.java b/bdi-jade/src/bdi4jade/core/BDIAgent.java
index f5db993..a5a59a0 100644
--- a/bdi-jade/src/bdi4jade/core/BDIAgent.java
+++ b/bdi-jade/src/bdi4jade/core/BDIAgent.java
@@ -38,6 +38,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import bdi4jade.annotation.GoalOwner;
import bdi4jade.belief.Belief;
import bdi4jade.core.GoalUpdateSet.GoalDescription;
import bdi4jade.event.GoalEvent;
@@ -47,6 +48,7 @@ import bdi4jade.goal.GoalStatus;
import bdi4jade.goal.Softgoal;
import bdi4jade.message.BDIAgentMsgReceiver;
import bdi4jade.plan.Plan;
+import bdi4jade.util.ReflectionUtils;
/**
* This class is an extension of {@link Agent} that has a current set of goals,
@@ -124,7 +126,8 @@ public class BDIAgent extends Agent {
.getGeneratedGoals()) {
Intention intention = addIntention(goal.getDispatcher(),
goal.getGoal(), null);
- agentGoalUpdateSet.addIntention(intention);
+ if (intention != null)
+ agentGoalUpdateSet.addIntention(intention);
}
for (GoalUpdateSet goalUpdateSet : capabilityGoalUpdateSets
.values()) {
@@ -132,7 +135,8 @@ public class BDIAgent extends Agent {
.getGeneratedGoals()) {
Intention intention = addIntention(
goal.getDispatcher(), goal.getGoal(), null);
- goalUpdateSet.addIntention(intention);
+ if (intention != null)
+ goalUpdateSet.addIntention(intention);
}
}
@@ -228,6 +232,7 @@ public class BDIAgent extends Agent {
private Set<Capability> capabilities;
protected final List<GoalListener> goalListeners;
protected final Log log;
+ private Map<Class<? extends Capability>, Set<Capability>> restrictedAccessOwnersMap;
private final Set<Softgoal> softgoals;
/**
@@ -237,6 +242,7 @@ public class BDIAgent extends Agent {
this.log = LogFactory.getLog(this.getClass());
this.bdiInterpreter = new BDIInterpreter(this);
this.capabilities = new HashSet<>();
+ this.restrictedAccessOwnersMap = new HashMap<>();
this.allIntentions = new HashMap<>();
this.aggregatedCapabilities = new HashSet<>();
this.agentIntentions = new LinkedList<>();
@@ -291,6 +297,7 @@ public class BDIAgent extends Agent {
synchronized (aggregatedCapabilities) {
this.aggregatedCapabilities.add(capability);
resetAllCapabilities();
+ computeGoalOwnersMap();
}
}
@@ -370,8 +377,15 @@ public class BDIAgent extends Agent {
*/
private final Intention addIntention(Capability dispatcher, Goal goal,
GoalListener goalListener) {
+ Intention intention = null;
+ try {
+ intention = new Intention(goal, this, dispatcher);
+ } catch (IllegalAccessException exc) {
+ log.error(exc);
+ return null;
+ }
+
synchronized (allIntentions) {
- Intention intention = new Intention(goal, this, dispatcher);
this.allIntentions.put(goal, intention);
if (dispatcher == null) {
agentIntentions.add(intention);
@@ -399,6 +413,13 @@ public class BDIAgent extends Agent {
}
}
+ private void computeGoalOwnersMap() {
+ this.restrictedAccessOwnersMap = new HashMap<>();
+ for (Capability capability : aggregatedCapabilities) {
+ ReflectionUtils.addGoalOwner(restrictedAccessOwnersMap, capability);
+ }
+ }
+
/**
* Drops a given goal of this agent, which means setting it as no longer
* desired. If the goal is not part of the agent's current goals, no action
@@ -584,6 +605,25 @@ public class BDIAgent extends Agent {
}
/**
+ * Returns the capability instances that owns a dispatched goal, considering
+ * the aggregated capabilities of this agent.
+ *
+ * If this method returns an empty set, it means that this agent cannot add
+ * a goal without the scope of a dispatcher that has access to it.
+ *
+ * @param owner
+ * the annotation with the goal owner.
+ * @return the capability instances related to this capability that owns the
+ * goal, or an empty set if the agent cannot add this goal.
+ */
+ public Set<Capability> getGoalOwner(GoalOwner owner) {
+ Set<Capability> restrictedAccessOwners = restrictedAccessOwnersMap
+ .get(owner.capability());
+ return restrictedAccessOwners == null ? new HashSet<Capability>()
+ : restrictedAccessOwners;
+ }
+
+ /**
* Returns all agent intentions, which are goals that this agent is
* committed to achieve.
*
@@ -622,6 +662,7 @@ public class BDIAgent extends Agent {
boolean removed = this.aggregatedCapabilities.remove(capability);
if (removed) {
resetAllCapabilities();
+ computeGoalOwnersMap();
}
return removed;
}
bdi-jade/src/bdi4jade/core/Capability.java 94(+93 -1)
diff --git a/bdi-jade/src/bdi4jade/core/Capability.java b/bdi-jade/src/bdi4jade/core/Capability.java
index 4fe0020..395a0e2 100644
--- a/bdi-jade/src/bdi4jade/core/Capability.java
+++ b/bdi-jade/src/bdi4jade/core/Capability.java
@@ -25,15 +25,19 @@ package bdi4jade.core;
import jade.lang.acl.ACLMessage;
import java.io.Serializable;
+import java.security.acl.Owner;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import bdi4jade.annotation.GoalOwner;
import bdi4jade.belief.Belief;
import bdi4jade.belief.BeliefBase;
import bdi4jade.core.GoalUpdateSet.GoalDescription;
@@ -48,6 +52,7 @@ import bdi4jade.reasoning.DefaultPlanSelectionStrategy;
import bdi4jade.reasoning.DeliberationFunction;
import bdi4jade.reasoning.OptionGenerationFunction;
import bdi4jade.reasoning.PlanSelectionStrategy;
+import bdi4jade.util.ReflectionUtils;
/**
* This capability represents a component that aggregates the mental attitudes
@@ -65,16 +70,18 @@ public class Capability implements Serializable {
protected final BeliefBase beliefBase;
private BeliefRevisionStrategy beliefRevisionStrategy;
private DeliberationFunction deliberationFunction;
+ private Map<Class<? extends Capability>, Set<Capability>> fullAccessOwnersMap;
protected final String id;
private final Collection<Intention> intentions;
protected final Log log;
private BDIAgent myAgent;
private OptionGenerationFunction optionGenerationFunction;
+ private final List<Class<? extends Capability>> parentCapabilities;
private final Set<Capability> partCapabilities;
protected final PlanLibrary planLibrary;
private PlanSelectionStrategy planSelectionStrategy;
+ private Map<Class<? extends Capability>, Set<Capability>> restrictedAccessOwnersMap;
private boolean started;
-
private Capability wholeCapability;
/**
@@ -137,6 +144,7 @@ public class Capability implements Serializable {
Set<Plan> initialPlans) {
this.log = LogFactory.getLog(getClass());
this.intentions = new LinkedList<>();
+ this.parentCapabilities = generateParentCapabilities();
// Id initialization
if (id == null) {
@@ -169,6 +177,8 @@ public class Capability implements Serializable {
this.deliberationFunction = new DefaultDeliberationFunction();
this.planSelectionStrategy = new DefaultPlanSelectionStrategy();
+ computeGoalOwnersMap();
+
synchronized (this) {
this.started = false;
}
@@ -203,6 +213,7 @@ public class Capability implements Serializable {
public final void addAssociatedCapability(Capability capability) {
this.associationTargets.add(capability);
capability.associationSources.add(this);
+ computeGoalOwnersMap();
resetAgentCapabilities();
}
@@ -244,6 +255,7 @@ public class Capability implements Serializable {
partCapability.wholeCapability = this;
this.partCapabilities.add(partCapability);
+ computeGoalOwnersMap();
resetAgentCapabilities();
}
@@ -270,6 +282,21 @@ public class Capability implements Serializable {
return false;
}
+ private void computeGoalOwnersMap() {
+ this.fullAccessOwnersMap = new HashMap<>();
+ ReflectionUtils.addGoalOwner(fullAccessOwnersMap, this);
+ if (wholeCapability != null) {
+ ReflectionUtils.addGoalOwner(fullAccessOwnersMap, this);
+ }
+ this.restrictedAccessOwnersMap = new HashMap<>();
+ for (Capability capability : associationTargets) {
+ ReflectionUtils.addGoalOwner(restrictedAccessOwnersMap, capability);
+ }
+ for (Capability capability : partCapabilities) {
+ ReflectionUtils.addGoalOwner(restrictedAccessOwnersMap, capability);
+ }
+ }
+
/**
* Returns true if the object given as parameter is a capability and has the
* same full id of this capability.
@@ -320,6 +347,19 @@ public class Capability implements Serializable {
this.optionGenerationFunction.generateGoals(goalUpdateSet);
}
+ @SuppressWarnings("unchecked")
+ private List<Class<? extends Capability>> generateParentCapabilities() {
+ List<Class<? extends Capability>> parentCapabilities = new LinkedList<>();
+ Class<?> currentClass = this.getClass();
+ while (Capability.class.isAssignableFrom(currentClass)
+ && !Capability.class.equals(currentClass)) {
+ parentCapabilities.add((Class<Capability>) currentClass
+ .getSuperclass());
+ currentClass = currentClass.getSuperclass();
+ }
+ return parentCapabilities;
+ }
+
/**
* Returns all capabilities with which this capability is associated.
*
@@ -377,6 +417,47 @@ public class Capability implements Serializable {
}
/**
+ * Returns the capability instances that owns a dispatched goal, considering
+ * the superclasses of this capability, its associations and compositions.
+ *
+ * A capability may dispatch its own goals and goals of its parents. It may
+ * also dispatch external goals of associated or part capabilities (and
+ * their parents), and all goals of whole capabilities.
+ *
+ * This method thus searches all capabilities that have a relationship with
+ * this capability (either inheritance, composition or association) and
+ * finds the concrete capability instances whose definition owns a goal
+ * (specified with the {@link Owner} annotation in goals).
+ *
+ * If this method returns an empty set, it means that this capability has no
+ * access to the goal owned by capabilities of the given class.
+ *
+ * @param owner
+ * the annotation with the goal owner.
+ * @return the capability instances related to this capability (or the
+ * capability itself) that owns the goal, or an empty set if the
+ * capability has no access to goals owned by capability of the
+ * given class.
+ */
+ public Set<Capability> getGoalOwner(GoalOwner owner) {
+ Set<Capability> owners = new HashSet<>();
+
+ Set<Capability> fullAccessOwners = fullAccessOwnersMap.get(owner
+ .capability());
+ if (fullAccessOwners != null)
+ owners.addAll(fullAccessOwners);
+
+ if (!owner.internal()) {
+ Set<Capability> restrictedAccessOwners = restrictedAccessOwnersMap
+ .get(owner.capability());
+ if (restrictedAccessOwners != null)
+ owners.addAll(restrictedAccessOwners);
+ }
+
+ return owners;
+ }
+
+ /**
* Returns this capability id.
*
* @return the id.
@@ -408,6 +489,15 @@ public class Capability implements Serializable {
}
/**
+ * Returns the classes of all parent capabilities of this capability.
+ *
+ * @return the parentCapabilities.
+ */
+ public List<Class<? extends Capability>> getParentCapabilities() {
+ return parentCapabilities;
+ }
+
+ /**
* Returns the parts of this capability.
*
* @return the partCapabilities.
@@ -460,6 +550,7 @@ public class Capability implements Serializable {
public final void removeAssociatedCapability(Capability capability) {
this.associationTargets.remove(capability);
capability.associationSources.remove(this);
+ computeGoalOwnersMap();
resetAgentCapabilities();
}
@@ -480,6 +571,7 @@ public class Capability implements Serializable {
boolean removed = this.partCapabilities.remove(partCapability);
if (removed) {
partCapability.wholeCapability = null;
+ computeGoalOwnersMap();
resetAgentCapabilities();
}
return removed;
bdi-jade/src/bdi4jade/core/Intention.java 31(+27 -4)
diff --git a/bdi-jade/src/bdi4jade/core/Intention.java b/bdi-jade/src/bdi4jade/core/Intention.java
index 0722bc1..c4da57d 100644
--- a/bdi-jade/src/bdi4jade/core/Intention.java
+++ b/bdi-jade/src/bdi4jade/core/Intention.java
@@ -32,6 +32,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import bdi4jade.annotation.GoalOwner;
import bdi4jade.event.GoalListener;
import bdi4jade.exception.PlanInstantiationException;
import bdi4jade.goal.Goal;
@@ -76,7 +77,8 @@ public class Intention {
* @param bdiAgent
* the bdiAgent associated with this intention.
*/
- public Intention(Goal goal, BDIAgent bdiAgent) {
+ public Intention(Goal goal, BDIAgent bdiAgent)
+ throws IllegalAccessException {
this(goal, bdiAgent, null);
}
@@ -92,7 +94,8 @@ public class Intention {
* @param dispatcher
* the Capability that dispatched the goal.
*/
- public Intention(Goal goal, BDIAgent bdiAgent, Capability dispatcher) {
+ public Intention(Goal goal, BDIAgent bdiAgent, Capability dispatcher)
+ throws IllegalAccessException {
this.log = LogFactory.getLog(this.getClass());
this.goal = goal;
this.myAgent = bdiAgent;
@@ -101,9 +104,29 @@ public class Intention {
this.waiting = true;
this.executedPlans = new HashSet<>();
this.currentPlan = null;
- this.dispatcher = dispatcher;
- this.owners = new HashSet<>(); // TODO
this.goalListeners = new LinkedList<>();
+ this.dispatcher = dispatcher;
+
+ GoalOwner owner = null;
+ if (goal.getClass().isAnnotationPresent(GoalOwner.class)) {
+ owner = goal.getClass().getAnnotation(GoalOwner.class);
+ }
+ if (owner == null) {
+ this.owners = new HashSet<>();
+ } else {
+ if (dispatcher == null) {
+ this.owners = myAgent.getGoalOwner(owner);
+ } else {
+ this.owners = dispatcher.getGoalOwner(owner);
+ if (owners.isEmpty()) {
+ throw new IllegalAccessException("Capability " + dispatcher
+ + " has no access to goal "
+ + goal.getClass().getSimpleName()
+ + " of capability "
+ + owner.getClass().getSimpleName());
+ }
+ }
+ }
}
/**
diff --git a/bdi-jade/src/bdi4jade/util/ReflectionUtils.java b/bdi-jade/src/bdi4jade/util/ReflectionUtils.java
index 13bf193..5e9920f 100644
--- a/bdi-jade/src/bdi4jade/util/ReflectionUtils.java
+++ b/bdi-jade/src/bdi4jade/util/ReflectionUtils.java
@@ -23,12 +23,16 @@
package bdi4jade.util;
import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import bdi4jade.annotation.Parameter;
import bdi4jade.annotation.Parameter.Direction;
+import bdi4jade.core.Capability;
import bdi4jade.exception.ParameterException;
import bdi4jade.goal.Goal;
import bdi4jade.plan.planbody.PlanBody;
@@ -46,6 +50,29 @@ public abstract class ReflectionUtils {
private static final Log log = LogFactory.getLog(ReflectionUtils.class);
private static final String SETTER_PREFIX = "set";
+ // TODO
+ public static void addGoalOwner(
+ Map<Class<? extends Capability>, Set<Capability>> goalOwnersMap,
+ Capability capability) {
+ addGoalOwner(goalOwnersMap, capability.getClass(), capability);
+ for (Class<? extends Capability> parentCapability : capability
+ .getParentCapabilities()) {
+ addGoalOwner(goalOwnersMap, parentCapability, capability);
+ }
+ }
+
+ // TODO
+ public static void addGoalOwner(
+ Map<Class<? extends Capability>, Set<Capability>> goalOwnersMap,
+ Class<? extends Capability> cababilityClass, Capability capability) {
+ Set<Capability> owners = goalOwnersMap.get(cababilityClass);
+ if (owners == null) {
+ owners = new HashSet<>();
+ goalOwnersMap.put(cababilityClass, owners);
+ }
+ owners.add(capability);
+ }
+
private static void checkSkipIsOK(Parameter parameter, String msg,
Exception cause) throws ParameterException {
if (parameter.mandatory()) {