io.coala.enterprise.role.AbstractActorRole.java Source code

Java tutorial

Introduction

Here is the source code for io.coala.enterprise.role.AbstractActorRole.java

Source

/* $Id: 9e2e41927242e225924e6fc5190c3e65aaadb0c6 $
 * $URL: https://dev.almende.com/svn/abms/enterprise-ontology/src/main/java/io/coala/enterprise/role/AbstractActorRole.java $
 * 
 * Part of the EU project Adapt4EE, see http://www.adapt4ee.eu/
 * 
 * @license
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 * Copyright (c) 2010-2014 Almende B.V. 
 */
package io.coala.enterprise.role;

import io.coala.agent.Agent;
import io.coala.agent.AgentID;
import io.coala.agent.AgentStatusUpdate;
import io.coala.bind.Binder;
import io.coala.capability.AbstractCapability;
import io.coala.capability.CapabilityID;
import io.coala.capability.admin.CreatingCapability;
import io.coala.capability.admin.DestroyingCapability;
import io.coala.capability.configure.ConfiguringCapability;
import io.coala.capability.interact.ReceivingCapability;
import io.coala.capability.interact.SendingCapability;
import io.coala.capability.know.ReasoningCapability;
import io.coala.capability.plan.SchedulingCapability;
import io.coala.capability.replicate.RandomizingCapability;
import io.coala.capability.replicate.ReplicatingCapability;
import io.coala.config.PropertyGetter;
import io.coala.enterprise.fact.CoordinationFact;
import io.coala.enterprise.fact.CoordinationFactType;
import io.coala.enterprise.organization.Organization;
import io.coala.exception.CoalaExceptionFactory;
import io.coala.factory.ClassUtil;
import io.coala.invoke.ProcedureCall;
import io.coala.invoke.Schedulable;
import io.coala.log.InjectLogger;
import io.coala.log.LogUtil;
import io.coala.message.Message;
import io.coala.model.ModelComponent;
import io.coala.model.ModelComponentIDFactory;
import io.coala.process.Job;
import io.coala.random.RandomDistribution;
import io.coala.time.SimTime;
import io.coala.time.SimTimeFactory;
import io.coala.time.TimeUnit;
import io.coala.time.Trigger;

import java.util.List;
import java.util.concurrent.CountDownLatch;

import javax.inject.Inject;
//import javax.inject.Named;
import javax.inject.Named;

import org.apache.log4j.Logger;

import rx.Observable;
import rx.Observer;
import rx.subjects.ReplaySubject;
import rx.subjects.Subject;

import com.fasterxml.jackson.annotation.JsonIgnore;

/**
 * {@link AbstractActorRole}
 * 
 * @version $Revision: 330 $
 * @author <a href="mailto:Rick@almende.org">Rick</a>
 * @param <F> the (super)type of {@link CoordinationFact} being handled
 */
public abstract class AbstractActorRole<F extends CoordinationFact> extends AbstractCapability<CapabilityID>
        implements ActorRole<F> {

    /** */
    private static final long serialVersionUID = 1L;

    /** */
    @InjectLogger
    private Logger LOG;// = LogUtil.getLogger(AbstractActorRole.class);;

    /** */
    @SuppressWarnings("rawtypes")
    @Inject
    @Named(Binder.AGENT_TYPE)
    private Class ownerType;

    /** the type of {@link CoordinationFact} */
    @JsonIgnore
    private Class<F> factType;

    /** */
    @JsonIgnore
    private final Observable<F> facts;

    /**
     * {@link AbstractActorRole} constructor
     * 
     * @param id
     * @param owner
     */
    @SuppressWarnings("unchecked")
    @Inject
    protected AbstractActorRole(final Binder binder) {
        super(null, binder);
        setID(new ActorRoleID(binder.getID(), getClass()));

        final List<Class<?>> typeArgs = ClassUtil.getTypeArguments(AbstractActorRole.class, getClass());
        this.factType = (Class<F>) typeArgs.get(0);
        // LOG.trace("Listening for messages of type: " +
        // this.factType.getName());
        this.facts = getReceiver().getIncoming().ofType(this.factType);
        this.facts.subscribe(new Observer<F>() {
            @Override
            public void onNext(final F fact) {
                final SimTime now = getTime();
                // LOG.trace("SCHEDULING FACT: " + fact + " AT " + now);
                getSimulator().schedule(
                        ProcedureCall.create(AbstractActorRole.this, AbstractActorRole.this, FACT_HANDLER, fact),
                        Trigger.createAbsolute(now));
                // LOG.trace("SCHEDULED FACT: " + fact + " AT " + now);
            }

            @Override
            public void onCompleted() {
                //
            }

            @Override
            public void onError(final Throwable e) {
                e.printStackTrace();
            }
        });
    }

    /** @param fact the ignored {@link CoordinationFact} to log */
    protected void logIgnore(final F fact, final boolean expired) {
        final CoordinationFactType factType = fact.getID().getType();
        final ActorRoleType roleType = expired ? factType.originatorRoleType() : factType.responderRoleType();
        final CoordinationFactType proceedType = factType.getDefaultResponse(roleType, true).outcome();
        final CoordinationFactType receedType = factType.getDefaultResponse(roleType, false).outcome();
        LOG.trace(String.format("%s ignoring %s (%s), default response type: " + "%s to proceed or %s otherwise",
                roleType, (expired ? "expiration of " : "") + factType, fact.getClass().getSimpleName(),
                proceedType, receedType));
    }

    @Override
    public ActorRoleID getID() {
        return (ActorRoleID) super.getID();
    }

    /** @see ModelComponent#getOwnerID() */
    @Override
    public AgentID getOwnerID() {
        return getID().getOwnerID();
    }

    /** @return the type of this {@link ActorRole}'s owner {@link Organization} */
    @SuppressWarnings("unchecked")
    public Class<? extends Organization> getOwnerType() {
        return (Class<? extends Organization>) this.ownerType;
    }

    /*   private class FactHandler implements Observer<F>
       {
     *//** @see Observer#onError(Throwable) */
    /*
    @Override
    public void onError(final Throwable t)
    {
    t.printStackTrace();
    }
        
    *//** @see Observer#onNext(Object) */

    /*
    @Override
    public void onNext(final F fact)
    {
    // don't handle immediately, may still be constructing the role!
    final SimTime now = getTime();
    LOG.info("SCHEDULING FACT:" + fact + " AT " + now);
    getSimulator().schedule(
    ProcedureCall.create(AbstractActorRole.this,
    AbstractActorRole.this, FACT_HANDLER, fact),
    Trigger.createAbsolute(now));
    };
        
    *//** @see Observer#onCompleted() */
    /*
    @Override
    public void onCompleted()
    {
    // empty
    }
    }
    */
    private static final String FACT_HANDLER = "factHandler";

    @Schedulable(FACT_HANDLER)
    public void handleFact(F fact) {
        // System.err.println("HANDLING FACT:" + fact);
        try {
            switch (fact.getID().getType()) {
            case ACCEPTED:
                asExecutor().onAccepted(fact);
                break;
            case QUIT:
                asExecutor().onQuit(fact);
                break;
            case REJECTED:
                asExecutor().onRejected(fact);
                break;
            case REQUESTED:
                asExecutor().onRequested(fact);
                break;
            case STOPPED:
                asExecutor().onStopped(fact);
                break;
            case _ALLOWED_PROMISE_CANCELLATION:
                asExecutor().onAllowedPromiseCancellation(fact);
                break;
            case _ALLOWED_STATE_CANCELLATION:
                asExecutor().onAllowedStateCancellation(fact);
                break;
            case _CANCELLED_ACCEPT:
                asExecutor().onCancelledAccept(fact);
                break;
            case _CANCELLED_REQUEST:
                asExecutor().onCancelledRequest(fact);
                break;
            case _REFUSED_PROMISE_CANCELLATION:
                asExecutor().onRefusedPromiseCancellation(fact);
                break;
            case _REFUSED_STATE_CANCELLATION:
                asExecutor().onRefusedStateCancellation(fact);
                break;

            case DECLINED:
                asInitiator().onDeclined(fact);
                break;
            case PROMISED:
                asInitiator().onPromised(fact);
                break;
            case STATED:
                asInitiator().onStated(fact);
                break;
            case _ALLOWED_ACCEPT_CANCELLATION:
                asInitiator().onAllowedAcceptCancellation(fact);
                break;
            case _ALLOWED_REQUEST_CANCELLATION:
                asInitiator().onAllowedRequestCancellation(fact);
                break;
            case _CANCELLED_PROMISE:
                asInitiator().onCancelledPromise(fact);
                break;
            case _CANCELLED_STATE:
                asInitiator().onCancelledState(fact);
                break;
            case _REFUSED_ACCEPT_CANCELLATION:
                asInitiator().onRefusedAcceptCancellation(fact);
                break;
            case _REFUSED_REQUEST_CANCELLATION:
                asInitiator().onRefusedRequestCancellation(fact);
                break;

            default:
                throw CoalaExceptionFactory.VALUE_NOT_ALLOWED.createRuntime("factType", fact.getID().getType());
            }
        } catch (final Throwable t) {
            onError(t);
        }
    }

    /**
     * @param value
     * @return
     */
    protected AgentID newAgentID(final String value) {
        return getBinder().inject(ModelComponentIDFactory.class).createAgentID(value);
    }

    /**
     * @return
     */
    protected RandomDistribution.Factory newDist() {
        return getBinder().inject(RandomDistribution.Factory.class);
    }

    /**
     * @param value
     * @param unit
     * @return
     */
    protected SimTime newTime(final Number value, final TimeUnit unit) {
        return getBinder().inject(SimTimeFactory.class).create(value, unit);
    }

    /** @see ActorRole#getTime() */
    @Override
    public SimTime getTime() {
        return getSimulator().getTime();
    }

    /** @see ActorRole#replayFacts() */
    @Override
    public Observable<F> replayFacts() {
        return this.facts.asObservable();
    }

    private Logger LOG() {

        // @InjectLogger doesn't work on injected (abstract) super types
        if (LOG == null) {
            LOG = LogUtil.getLogger(AbstractActorRole.class, this);
            LOG.info("Logger NOT INJECTED");
        }
        return LOG;
    }

    /** @see ActorRole#onStopped(CoordinationFact) */
    protected void onStopped(final F fact) {
        LOG().warn("Ignoring " + fact.getID().getType() + ": " + fact);
    }

    /** @see ActorRole#onQuit(CoordinationFact) */
    protected void onQuit(final F fact) {
        LOG().warn("Ignoring " + fact.getID().getType() + ": " + fact);
    }

    private AbstractInitiator<F> asInitiator() {
        return (AbstractInitiator<F>) this;
    }

    private AbstractExecutor<F> asExecutor() {
        return (AbstractExecutor<F>) this;
    }

    private static final String ADD_PROCESS_MANAGER_AGENT = "addProcessManagerAgent";

    @Schedulable(ADD_PROCESS_MANAGER_AGENT)
    protected Observable<AgentStatusUpdate> bootAgent(final AgentID agentID, final Class<? extends Agent> agentType,
            // final BasicAgentStatus blockSimUntilState,
            final Job<?> next) throws Exception {
        if (next == null) // no need to sleep sim, nothing to schedule next
            return getBooter().createAgent(agentID, agentType);

        final CountDownLatch latch = new CountDownLatch(1);
        final Subject<AgentStatusUpdate, AgentStatusUpdate> status = ReplaySubject.create();
        status.subscribe(new Observer<AgentStatusUpdate>() {
            /** */
            private boolean success = false;

            @Override
            public void onNext(final AgentStatusUpdate update) {
                LOG().trace("Got child agent update: " + update);
                if (update.getStatus().isFailedStatus()) {
                    LOG().warn("Child agent failed: " + update.getAgentID());
                    latch.countDown();
                } else if (update.getStatus().isInitializedStatus()// .equals(blockSimUntilState)
                ) {
                    LOG().info("Child agent " + agentID + " reached unblock status: " + update.getStatus());
                    success = true;
                    latch.countDown(); // yield
                }
            }

            @Override
            public void onCompleted() {
                if (success)
                    return;
                LOG().warn("Child agent died but never reached blockable status" + ", scheduling next job now");
                latch.countDown();
            }

            @Override
            public void onError(final Throwable e) {
                e.printStackTrace();
            }
        });
        getBooter().createAgent(agentID, agentType).subscribe(status);
        //while (latch.getCount() > 0) {
        latch.await(1000L, java.util.concurrent.TimeUnit.MILLISECONDS);
        //   Thread.yield();
        //}
        if (latch.getCount() > 0) {
            LOG.error("FIXME: Failed to wait for agent boot signal for " + agentID + ", continue unsafe.",
                    new IllegalStateException("No initialize or fail status for agent reached during boot."));
            getScheduler().schedule(
                    ProcedureCall.create(this, this, ADD_PROCESS_MANAGER_AGENT, agentID, agentType, next),
                    Trigger.createAbsolute(getTime()));
        } else
            getSimulator().schedule(next, Trigger.createAbsolute(getTime()));

        return status.asObservable();
    }

    /**
     * @return the (super)type of {@link CoordinationFact}
     */
    protected Class<F> getFactType() {
        return this.factType;
    }

    /** @see ActorRole#send(CoordinationFact) */
    protected <M extends Message<?>> M send(final M fact) throws Exception {
        return send(0, fact);
    }

    /** @see ActorRole#send(CoordinationFact) */
    protected <M extends Message<?>> M send(final Number delay, final M fact) throws Exception {
        return send(newTime(delay, getTime().getUnit()), fact);
    }

    /** @see ActorRole#send(CoordinationFact) */
    protected <M extends Message<?>> M send(final SimTime delay, final M fact) throws Exception {
        // LOG.trace("Sending fact: " + fact);
        getSimulator().schedule(ProcedureCall.create(this, this, SEND_METHOD_ID, fact),
                Trigger.createAbsolute(getTime().plus(delay)));
        return fact;
    }

    private static final String SEND_METHOD_ID = "actorRoleSend";

    /** @see ActorRole#send(CoordinationFact) */
    @Schedulable(SEND_METHOD_ID)
    private <M extends Message<?>> M doSend(final M fact) throws Exception {
        getMessenger().send(fact);
        return fact;
    }

    /** @return the agent's local {@link BooterService} */
    @JsonIgnore
    protected CreatingCapability getBooter() {
        return getBinder().inject(CreatingCapability.class);
    }

    /** @return the agent's local {@link SimulatorService} */
    @JsonIgnore
    protected SchedulingCapability<SimTime> getScheduler() {
        return getSimulator();
    }

    /** @return the agent's local {@link SimulatorService} */
    @JsonIgnore
    protected ReplicatingCapability getSimulator() {
        return getBinder().inject(ReplicatingCapability.class);
    }

    /** @return the agent's local {@link MessengerService} */
    @JsonIgnore
    protected SendingCapability getMessenger() {
        return getBinder().inject(SendingCapability.class);
    }

    /** @return the agent's local {@link ReceiverService} */
    @JsonIgnore
    protected ReceivingCapability getReceiver() {
        return getBinder().inject(ReceivingCapability.class);
    }

    /**
     * @param key the configuration value to get
     * @return the {@link PropertyGetter} from agent's local
     *         {@link ConfigurerService}
     */
    protected PropertyGetter getProperty(final String key) {
        return getBinder().inject(ConfiguringCapability.class).getProperty(key);
    }

    /** @return the agent's local {@link ReasonerService} */
    @JsonIgnore
    protected ReasoningCapability getReasoner() {
        return getBinder().inject(ReasoningCapability.class);
    }

    @JsonIgnore
    protected DestroyingCapability getFinalizer() {
        return getBinder().inject(DestroyingCapability.class);
    }

    /** @return the agent's local {@link RandomizerService} */
    @JsonIgnore
    protected RandomizingCapability getRandomizer() {
        return getSimulator();// getBinder().bind(RandomizerService.class);
    }

    // /** @return the agent's local {@link EmbodierService} */
    // @JsonIgnore
    // protected EmbodierService getWorld()
    // {
    // return getBinder().inject(EmbodierService.class);
    // }

}