/* * codjo.net * * Common Apache License 2.0 */ package net.codjo.segmentation.server.blackboard; import net.codjo.agent.AclMessage; import net.codjo.agent.Behaviour; import net.codjo.agent.DFService; import net.codjo.agent.MessageTemplate; import static net.codjo.agent.MessageTemplate.matchPerformative; import net.codjo.agent.UserId; import net.codjo.agent.protocol.FailureException; import net.codjo.agent.protocol.NotUnderstoodException; import net.codjo.agent.protocol.SubscribeParticipant; import net.codjo.agent.protocol.SubscribeParticipantHandler; import net.codjo.segmentation.server.blackboard.message.BlackboardAction; import net.codjo.segmentation.server.blackboard.message.BlackboardActionVisitor; import net.codjo.segmentation.server.blackboard.message.Erase; import net.codjo.segmentation.server.blackboard.message.GetTodo; import net.codjo.segmentation.server.blackboard.message.InformOfFailure; import net.codjo.segmentation.server.blackboard.message.Level; import net.codjo.segmentation.server.blackboard.message.MessageCodec; import net.codjo.segmentation.server.blackboard.message.Read; import net.codjo.segmentation.server.blackboard.message.Todo; import net.codjo.segmentation.server.blackboard.message.Write; import org.apache.log4j.Logger; /** * */ public class BlackboardBehaviour extends Behaviour { public static final String BLACKBOARD_SERVICE = "blackboard-service"; public static final String BLACKBOARD_PROTOCOL = "blackboard-protocol"; private static final Logger LOG = Logger.getLogger(BlackboardBehaviour.class); private State state = State.REGISTER_TO_DF; private MessageCodec codec = new MessageCodec(); private final MessageTemplate requestTemplate; private final ActionExecutor actionExecutor = new ActionExecutor(); private final BlackboardManager blackboardManager; private BlackboardListener listener; private UserId userId; public BlackboardBehaviour(Level... levels) { this(new BlackboardListenerAdapter(), levels); } public BlackboardBehaviour(BlackboardListener listener, Level... levels) { this.listener = listener; blackboardManager = new BlackboardManager(levels); requestTemplate = MessageTemplate.and(matchPerformative(AclMessage.Performative.REQUEST), MessageTemplate.matchProtocol(BLACKBOARD_PROTOCOL)); } @Override protected final void action() { switch (state) { case REGISTER_TO_DF: registerToDf(); state = State.START_LISTENNING_SUBSCRIPTION; break; case START_LISTENNING_SUBSCRIPTION: startListenningSubscription(); state = State.BLACKBOARD_RUNNING; break; case BLACKBOARD_RUNNING: listenBlackboardActionRequest(); break; } } @Override public final boolean done() { return false; } public void postTodo(Level level, Todo todo) { Level realLevel = blackboardManager.getLevel(level); blackboardManager.addTodo(realLevel, todo); listener.todoWrited(realLevel, todo); if (!blackboardManager.todoExists(realLevel, todo)) { return; } AclMessage inform = new AclMessage(AclMessage.Performative.INFORM); inform.setContent(realLevel.getName()); encodeUserId(inform); for (SubscribeParticipant.Subscription subscription : blackboardManager.getSubscription(level)) { subscription.reply(inform); } } public void removeTodo(Level level, Todo todo) { boolean previouslyFinished = blackboardManager.isFinished(); blackboardManager.removeTodo(level, todo); if (!actionExecutor.isRunning()) { checkIfBlackboardIsFinished(previouslyFinished); } } public void doNotRegisterToDf() { state = State.START_LISTENNING_SUBSCRIPTION; } public void setListener(BlackboardListener listener) { this.listener = listener; } public void setUserId(UserId userId) { this.userId = userId; } protected int getParticipantCount(Level level) { return blackboardManager.getSubscription(level).size(); } private void checkIfBlackboardIsFinished(boolean previouslyFinished) { if (blackboardManager.isFinished() && !previouslyFinished) { listener.blackboardFinished(blackboardManager.getLastTodos()); blackboardManager.reset(); } } private AclMessage encodeUserId(AclMessage aclMessage) { if (userId != null) { aclMessage.encodeUserId(userId); } return aclMessage; } private void startListenningSubscription() { SubscribeParticipant subscribe = new SubscribeParticipant(getAgent(), new SubscriptionHandler(), matchPerformative(AclMessage.Performative.SUBSCRIBE)); getAgent().addBehaviour(subscribe); } private void registerToDf() { try { DFService.ServiceDescription service = new DFService.ServiceDescription(BLACKBOARD_SERVICE, "blackboard"); DFService.register(getAgent(), new DFService.AgentDescription(service)); } catch (DFService.DFServiceException e) { LOG.fatal("Impossible de s'enregistrer aupr�s du DF", e); getAgent().die(); } } private void listenBlackboardActionRequest() { AclMessage aclMessage = getAgent().receive(requestTemplate); if (aclMessage == null) { block(); return; } boolean previouslyFinished = blackboardManager.isFinished(); actionExecutor.setRunning(true); try { BlackboardAction blackboardAction = codec.decode(aclMessage); if (LOG.isDebugEnabled()) { LOG.debug("Blackboard " + getAgent().getAID() + " traite les actions suivantes \n" + aclMessage.getContent()); } blackboardAction.acceptVisitor(actionExecutor.using(aclMessage)); checkIfBlackboardIsFinished(previouslyFinished && !actionExecutor.hasWroteTodos()); } catch (Throwable e) { LOG.error("Action en cours d'execution :\n" + aclMessage.toFipaACLString()); LOG.error("Erreur lors de l'execution des actions ", e); } finally { actionExecutor.setRunning(false); } } private class SubscriptionHandler implements SubscribeParticipantHandler { public void handleSubscribe(SubscribeParticipant.Subscription subscription) throws NotUnderstoodException { try { Level level = new Level(subscription.getMessage().getContent()); blackboardManager.addSubscription(level, subscription); subscription.reply(new AclMessage(AclMessage.Performative.AGREE)); } catch (Exception e) { throw new NotUnderstoodException("Echec lors de l'inscription de l'agent : " + subscription.getMessage().getSender()); } } public void handleCancel(SubscribeParticipant.Subscription subscription) throws FailureException { try { Level level = new Level(subscription.getMessage().getContent()); blackboardManager.removeSubscription(level, subscription); subscription.reply(encodeUserId(new AclMessage(AclMessage.Performative.INFORM))); } catch (Exception e) { throw new FailureException("Echec lors de la desinscription de l'agent : " + subscription.getMessage().getSender()); } } } private class ActionExecutor implements BlackboardActionVisitor { private AclMessage requestMessage; private boolean running = false; private boolean wroteTodos = false; public void visit(Write write) { wroteTodos = true; postTodo(write.getLevel(), write.getTodo()); } public void visit(GetTodo getTodo) { Todo todo = blackboardManager.startFirstTodo(getTodo.getLevel()); if (todo == null) { return; } AclMessage response = requestMessage.createReply(AclMessage.Performative.INFORM); codec.encodeRead(response, new Read(blackboardManager.getLevel(getTodo.getLevel()), todo)); getAgent().send(encodeUserId(response)); } public void visit(Erase erase) { blackboardManager.removeTodo(erase.getLevel(), erase.getTodo()); } public void visit(InformOfFailure informOfFailure) { listener.informOfFailure(blackboardManager.getLevel(informOfFailure.getLevel()), informOfFailure.getTodo(), informOfFailure.getErrorMessage()); } public BlackboardActionVisitor using(AclMessage aclMessage) { wroteTodos = false; this.requestMessage = aclMessage; return this; } public boolean isRunning() { return running; } public void setRunning(boolean running) { this.running = running; } public boolean hasWroteTodos() { return wroteTodos; } } private enum State { REGISTER_TO_DF, START_LISTENNING_SUBSCRIPTION, BLACKBOARD_RUNNING } }