es.csic.iiia.planes.behaviors.AbstractBehaviorAgent.java Source code

Java tutorial

Introduction

Here is the source code for es.csic.iiia.planes.behaviors.AbstractBehaviorAgent.java

Source

/*
 * Software License Agreement (BSD License)
 *
 * Copyright 2012 Marc Pujol <mpujol@iiia.csic.es>.
 *
 * Redistribution and use of this software in source and binary forms, with or
 * without modification, are permitted provided that the following conditions
 * are met:
 *
 *   Redistributions of source code must retain the above
 *   copyright notice, this list of conditions and the
 *   following disclaimer.
 *
 *   Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the
 *   following disclaimer in the documentation and/or other
 *   materials provided with the distribution.
 *
 *   Neither the name of IIIA-CSIC, Artificial Intelligence Research Institute
 *   nor the names of its contributors may be used to
 *   endorse or promote products derived from this
 *   software without specific prior written permission of
 *   IIIA-CSIC, Artificial Intelligence Research Institute
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package es.csic.iiia.planes.behaviors;

import es.csic.iiia.planes.AbstractMessagingAgent;
import es.csic.iiia.planes.Location;
import es.csic.iiia.planes.messaging.Message;
import es.csic.iiia.planes.util.DependencyResolver;
import org.apache.commons.collections.map.MultiKeyMap;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Skeletal implementation of a messaging agent.
 * <p/>
 * Agents using this implementation must react to received messages by using
 * {@link Behavior}s.
 *
 * @author Marc Pujol <mpujol@iiia.csic.es>
 */
public abstract class AbstractBehaviorAgent extends AbstractMessagingAgent {
    private static final Logger LOG = Logger.getLogger(AbstractBehaviorAgent.class.getName());

    /**
     * The communication radius.
     */
    private double communicationRange;

    /**
     * Messages received in the previous iteration, available at the current
     * time.
     */
    private List<Message> currentMessages;

    /**
     * Messages received in this iteration, that will not be available until
     * the next one.
     */
    private List<Message> futureMessages;

    /**
     * The list of behaviors of this agent.
     */
    private List<Behavior> behaviors;

    /**
     * Flag to prevent nodes from adding behaviors after being initialized.
     */
    private boolean initialized = false;

    public AbstractBehaviorAgent(Location location) {
        super(location);
        currentMessages = new ArrayList<Message>();
        futureMessages = new ArrayList<Message>();
        behaviors = new ArrayList<Behavior>();
    }

    @Override
    @SuppressWarnings("unchecked")
    public void initialize() {
        // Compute the behavior ordering from the declared dependencies
        DependencyResolver d = new DependencyResolver();
        for (Behavior v : behaviors) {
            Class[] dependencies = v.getDependencies();
            d.add(v.getClass(), dependencies);
        }

        // Get an ordered list of behavior classes, and construct a new
        // (ordered) list of behavior objects.
        List<Behavior> newBehaviors = new ArrayList<Behavior>(behaviors.size());
        for (Class c : d.getOrderedList()) {
            Behavior b = getBehavior(c);
            b.initialize();
            newBehaviors.add(b);
        }

        behaviors = newBehaviors;
        initialized = true;
    }

    /**
     * Add a new behavior to the agent.
     *
     * Every time a message is being received (which always happens during the
     * {@link #step() } execution), any behavior that implements a handler
     * of the specific type of the message will be triggered.
     *
     * @param behavior to be added.
     */
    protected void addBehavior(Behavior behavior) {
        if (initialized) {
            throw new UnsupportedOperationException(
                    "You can only add behaviors to an agent inside its constructor, not here.");
        }
        behaviors.add(behavior);
    }

    /**
     * Get the list of behaviors.
     * @return list of behaviors of this agent.
     */
    protected List<Behavior> getBehaviors() {
        return Collections.unmodifiableList(behaviors);
    }

    /**
     * Get the behavior that implements a specific class.
     *
     * @return behavior that implements the given class.
     */
    @SuppressWarnings("unchecked")
    public <T extends Behavior> T getBehavior(Class<T> behaviorClass) {
        for (Behavior b : behaviors) {
            if (b.getClass() == behaviorClass) {
                return (T) b;
            }
        }
        return null;
    }

    @Override
    public double getCommunicationRange() {
        return communicationRange;
    }

    @Override
    public void setCommunicationRange(double range) {
        this.communicationRange = range;
    }

    @Override
    public void receive(Message message) {
        futureMessages.add(message);
    }

    /**
     * {@inheritDoc}
     * <p/>
     * In this case, the step initialization is to turn the "future" messages
     * of the previous iteration into the "current" ones for this iteration.
     * <p/>
     * Additionally, it gives the opportunity for behaviors to initialize
     * themselves.
     */
    @Override
    public void preStep() {
        List<Message> tmp = currentMessages;
        tmp.clear();
        currentMessages = futureMessages;
        futureMessages = tmp;

        for (Behavior b : behaviors) {
            b.preStep();
        }
    }

    /**
     * Call the message handler for each message received in the previous
     * iteration.
     */
    @Override
    public void step() {

        for (Behavior b : behaviors) {
            b.beforeMessages();
        }

        dispatchMessages();

        for (Behavior b : behaviors) {
            b.afterMessages();
        }

    }

    /**
     * {@inheritDoc}
     * <p/>
     * In this case, it gives the opportunity for behaviors to finalize the
     * current iteration.
     */
    @Override
    public void postStep() {
        for (Behavior b : behaviors) {
            b.postStep();
        }
    }

    @Override
    public void send(Message message) {
        message.setSender(this);
        getWorld().sendMessage(message);
    }

    private void dispatchMessages() {
        LOG.log(Level.FINER, "{0} dispatching {1} messages.", new Object[] { this, currentMessages.size() });

        for (Behavior b : behaviors) {
            for (Message m : currentMessages) {
                handle(b, m);
            }
        }
    }

    private MultiKeyMap cache = new MultiKeyMap();

    private void handle(Behavior b, Message m) {
        final Class<? extends Behavior> bClass = b.getClass();
        final Class<? extends Message> mClass = m.getClass();

        // Memoize the method
        Method method;
        if (cache.containsKey(bClass, mClass)) {
            method = (Method) cache.get(bClass, mClass);
        } else {
            method = getMethod(bClass, mClass);
            if (method != null) {
                if (LOG.isLoggable(Level.FINEST)) {
                    LOG.log(Level.FINEST, "Dispatching {0} to {1}",
                            new Object[] { mClass.getSimpleName(), method.toGenericString() });
                }
            }
            cache.put(bClass, mClass, method);
        }

        if (method == null) {
            return;
        }

        // Invoke it
        try {
            method.invoke(b, m);
        } catch (IllegalAccessException ex) {
            LOG.log(Level.SEVERE, null, ex);
        } catch (IllegalArgumentException ex) {
            LOG.log(Level.SEVERE, "Error invoking " + method.toGenericString() + " on " + b + " with argument " + m,
                    ex);
        } catch (InvocationTargetException ex) {
            Throwable throwable = ex.getTargetException();
            if (throwable instanceof Error) {
                throw (Error) throwable;
            } else if (throwable instanceof RuntimeException) {
                throw (RuntimeException) throwable;
            }
            LOG.log(Level.SEVERE, null, throwable);
        }
    }

    @SuppressWarnings("unchecked")
    private static Method getMethod(Class<? extends Behavior> bClass, Class<? extends Message> mClass) {
        Method m = null;
        try {
            m = bClass.getMethod("on", mClass);
        } catch (NoSuchMethodException ex) {
            Class c = mClass.getSuperclass();
            if (Message.class.isAssignableFrom(c)) {
                m = getMethod(bClass, (Class<? extends Message>) c);
            }
        } catch (SecurityException ex) {
            LOG.log(Level.SEVERE, null, ex);
        }
        return m;
    }

}