All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.integratedmodelling.engine.ModelingEngine Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (C) 2007, 2015:
 * 
 * - Ferdinando Villa  - integratedmodelling.org - any
 * other authors listed in @author annotations
 *
 * All rights reserved. This file is part of the k.LAB software suite, meant to enable
 * modular, collaborative, integrated development of interoperable data and model
 * components. For details, see http://integratedmodelling.org.
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms
 * of the Affero General Public License Version 3 or any later version.
 *
 * This program is distributed in the hope that it will be useful, but without any
 * warranty; without even the implied warranty of merchantability or fitness for a
 * particular purpose. See the Affero General Public License for more details.
 * 
 * You should have received a copy of the Affero General Public License along with this
 * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite
 * 330, Boston, MA 02111-1307, USA. The license is also available at:
 * https://www.gnu.org/licenses/agpl.html
 *******************************************************************************/
package org.integratedmodelling.engine;

import java.io.File;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.integratedmodelling.api.annotations.SubjectType;
import org.integratedmodelling.api.auth.IUser;
import org.integratedmodelling.api.engine.ILock;
import org.integratedmodelling.api.engine.IModelingEngine;
import org.integratedmodelling.api.metadata.IModelMetadata;
import org.integratedmodelling.api.metadata.IObservationMetadata;
import org.integratedmodelling.api.modelling.IDirectObserver;
import org.integratedmodelling.api.modelling.IModelBean;
import org.integratedmodelling.api.modelling.IObservableSemantics;
import org.integratedmodelling.api.modelling.ISubject;
import org.integratedmodelling.api.modelling.resolution.IResolutionScope;
import org.integratedmodelling.api.monitoring.IMonitor;
import org.integratedmodelling.api.monitoring.Messages;
import org.integratedmodelling.api.network.IComponent;
import org.integratedmodelling.api.network.INode;
import org.integratedmodelling.api.runtime.IContext;
import org.integratedmodelling.api.runtime.ISession;
import org.integratedmodelling.api.runtime.ITask;
import org.integratedmodelling.api.runtime.ITask.Status;
import org.integratedmodelling.api.services.IPrototype;
import org.integratedmodelling.common.beans.Notification;
import org.integratedmodelling.common.beans.Task;
import org.integratedmodelling.common.client.ModelingClient;
import org.integratedmodelling.common.command.ServiceManager;
import org.integratedmodelling.common.configuration.KLAB;
import org.integratedmodelling.common.monitoring.Monitor;
import org.integratedmodelling.common.network.Broadcaster;
import org.integratedmodelling.common.network.EngineNetwork;
import org.integratedmodelling.common.project.Component;
import org.integratedmodelling.common.project.ProjectManager;
import org.integratedmodelling.common.project.Workspace;
import org.integratedmodelling.common.utils.Lock;
import org.integratedmodelling.common.utils.NameGenerator;
import org.integratedmodelling.common.vocabulary.authority.AuthorityFactory;
import org.integratedmodelling.engine.authorities.GACSAuthority;
import org.integratedmodelling.engine.authorities.GBIFAuthority;
import org.integratedmodelling.engine.authorities.IUPACAuthority;
import org.integratedmodelling.engine.authorities.WRBAuthority;
import org.integratedmodelling.engine.modelling.runtime.AbstractBaseTask;
import org.integratedmodelling.engine.modelling.runtime.Context;
import org.integratedmodelling.engine.modelling.runtime.EngineSession;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabIOException;

/**
 * A modeling engine is required to make any observation. This (and its client
 * counterpart, {@link ModelingClient} are the most likely to be used directly from Java
 * code as engine implementations.
 * 
 * @author ferdinando.villa
 *
 */
public class ModelingEngine extends Engine implements IModelingEngine {

    private Broadcaster    advertiser;
    private String         communicationChannel = "/engine/" + NameGenerator.shortUUID();
    private IMonitor       monitor              = new EngineMonitor();
    private List listeners            = new ArrayList<>();

    /**
     * The multicast notification bus where we can receive engine notifications and send
     * messages to client sessions.
     * 
     * @return the bus, or null if it was not started.
     */
    @Override
    public Broadcaster getNotificationBus() {
        return advertiser;
    }

    @Override
    public IMonitor getMonitor() {
        return monitor;
    }

    /*
     * prevent BS warnings from various libs.
     */
    static {
        System.setProperty("com.sun.media.jai.disableMediaLib", "true");
    }

    @Override
    public Collection getFunctionPrototypes() {

        List ret = ServiceManager.get().getFunctionPrototypes();

        Set ids = new HashSet<>();
        for (IPrototype p : ret) {
            ids.add(p.getId());
        }

        /*
         * in the engine, also add all prototypes from the network. Nodes just report
         * their own although getFunctionPrototype() will also return remote component
         * functions.
         */
        for (INode n : getNetwork().getNodes()) {
            for (IPrototype p : n.getFunctionPrototypes()) {
                /*
                 * don't override local prototypes with remote versions.
                 */
                if (!ids.contains(p.getId())) {
                    ret.add(p);
                }
            }
        }

        Collections.sort(ret, new Comparator() {

            @Override
            public int compare(IPrototype o1, IPrototype o2) {
                return o1.getId().compareTo(o2.getId());
            }
        });
        return ret;

    }

    /**
     * Only use this constructor if the certificate is set later, or for the very limited
     * operation without one.
     * 
     * @throws KlabException
     */
    public ModelingEngine() throws KlabException {
        super();
        this.network = new EngineNetwork();
    }

    /**
     * The regular constructor, parsing a user certificate and connecting to the network
     * through the access point in it.
     * 
     * @param certificate
     * @throws KlabException
     */
    public ModelingEngine(File certificate) throws KlabException {
        super();
        this.network = new EngineNetwork(certificate);
    }

    /**
     * Start the multicast of our IP address and identity so that clients can discover us.
     * This is done automatically in servers.
     * 
     * @param applicationId
     * @param port
     */
    public void startAdvertising(String applicationId, int port) {
        KLAB.info("Starting multicast of IP on cluster " + applicationId + " for port " + port);
        this.advertiser = new Broadcaster(KLAB.ENGINE.getName(), applicationId, port);
    }

    @Override
    protected final void startup() throws KlabException {

        super.startup();

        registerAnnotation(SubjectType.class, new AnnotationHandler() {
            @SuppressWarnings("unchecked")
            @Override
            public void processAnnotatedClass(Annotation annotation, Class cls) {
                String concept = ((SubjectType) annotation).value();
                if (ISubject.class.isAssignableFrom(cls)) {
                    KLAB.MMANAGER.registerSubjectClass(concept, (Class) cls);
                }
            }
        });

        scanPackage("org.integratedmodelling");

        this.lock = new Lock(".lck");

        KLAB.WORKSPACE = new Workspace(KLAB.CONFIG.getDataPath("workspace"), true);

        if (!((EngineNetwork) network).initialize()) {
            throw new KlabIOException("error initializing network. Check connectivity, online status, remote server status or certificate.");
        }

        ((ProjectManager) KLAB.PMANAGER).deployComponents();

        /**
         * Ensure that local workspace projects override those deployed from network. This
         * shouldn't happen as the workspace is smart about it, but the network
         * automatically registers anything downloaded - FIXME check that.
         */
        ((ProjectManager) KLAB.PMANAGER).overrideDeployed(KLAB.WORKSPACE);

        KLAB.PMANAGER.load(true, KLAB.MFACTORY.getRootParsingContext());

        KLAB.info("loading component knowledge");

        /**
         * Initialize them
         */
        for (IComponent component : KLAB.PMANAGER.getComponents()) {
            ((Component) component).initialize(monitor);
        }

        for (IComponent component : KLAB.PMANAGER.getComponents()) {
            KLAB.PMANAGER.loadComponent(component, KLAB.MFACTORY.getRootParsingContext());
        }
        
        /**
         * Create native authorities
         */
        AuthorityFactory.get().registerAuthority(GBIFAuthority.newInstance());
        AuthorityFactory.get().registerAuthority(IUPACAuthority.get());
        AuthorityFactory.get().registerAuthority(WRBAuthority.get());
        AuthorityFactory.get().registerAuthority(GACSAuthority.get());

    }

    /**
     * Entry point in Thinklab: call boot() before you do anything. Calling more than once
     * without calling shutdown() has no effect.
     * 
     * @throws KlabException
     */
    public static void boot() throws KlabException {
        if (KLAB.ENGINE == null) {
            KLAB.ENGINE = new ModelingEngine();
        }
        ((ModelingEngine) (KLAB.ENGINE)).startup();
    }

    @Override
    public void shutdown(int seconds) {

        if (lock.isLocked()) {
            lock.unlock(getUser());
        }

        if (advertiser != null) {
            advertiser.stop();
        }
        super.shutdown(seconds);
    }

    @SuppressWarnings("javadoc")
    public ILock getLock() {
        return lock;
    }

    @Override
    public String getName() {
        return KLAB.NAME;
    }

    @Override
    public String getUrl() {
        return network.getUrl();
    }

    @Override
    public boolean isRunning() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public String submitObservation(IDirectObserver observer, boolean store) throws KlabException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List queryObservations(String text, boolean localOnly) throws KlabException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List queryModels(IObservableSemantics observable, IResolutionScope context)
            throws KlabException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public ITask setupComponent(String componentId) throws KlabException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void removeObservations(Collection observationNames) throws KlabException {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean start() {
        try {
            extractKnowledge();
            startup();
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    @Override
    public boolean stop() {
        shutdown(0);
        return true;
    }

    /**
     * an engine that sends notifications through the multicast bus rather than logging.
     */
    public static class EngineMonitor extends Monitor {

        /*
         * get the STOMP channel for the currently monitored context
         */
        String getChannel() {
            String ret = KLAB.ENGINE.getName();
            if (getSession() != null) {
                ret += "/session/" + getSession().getId();
            } else if (KLAB.ENGINE instanceof IModelingEngine) {
                ret = ((IModelingEngine) KLAB.ENGINE).getCommunicationChannel();
            }
            return ret;
        }

        @Override
        public void info(Object info, String infoClass) {
            if (getSession() == null || ((ModelingEngine) KLAB.ENGINE).getNotificationBus() == null) {
                KLAB.info(info);
            } else {
                Notification n = getNotification();
                n.setBody(info == null ? null : info.toString());
                n.setNotificationClass(infoClass);
                n.defineLevel(Level.INFO);
                ((ModelingEngine) KLAB.ENGINE).getNotificationBus().send(n, getChannel());
            }
        }

        @Override
        public void warn(Object o) {
            if (getSession() == null || ((ModelingEngine) KLAB.ENGINE).getNotificationBus() == null) {
                KLAB.warn(o);
            } else {
                Notification n = getNotification();
                n.setBody(payloadToString(o));
                n.defineLevel(Level.WARNING);
                ((ModelingEngine) KLAB.ENGINE).getNotificationBus().send(n, getChannel());
            }
        }

        @Override
        public void error(Object o) {

            if (getTask() != null) {
                ((AbstractBaseTask) getTask()).setStatus(Status.ERROR);
                send(Messages.TASK_FAILED);
            }

            if (getSession() == null || ((ModelingEngine) KLAB.ENGINE).getNotificationBus() == null) {
                KLAB.error(o);
            } else {
                Notification n = getNotification();
                n.setBody(payloadToString(o));
                n.defineLevel(Level.SEVERE);
                ((ModelingEngine) KLAB.ENGINE).getNotificationBus().send(n, getChannel());
            }
        }

        @Override
        public void debug(Object o) {
            if (KLAB.CONFIG.isDebug()) {
                if (getSession() == null || ((ModelingEngine) KLAB.ENGINE).getNotificationBus() == null) {
                    KLAB.debug(o);
                } else {
                    Notification n = getNotification();
                    n.setBody(payloadToString(o));
                    n.defineLevel(Level.FINE);
                    ((ModelingEngine) KLAB.ENGINE).getNotificationBus().send(n, getChannel());
                }
            }
        }

        private String payloadToString(Object o) {
            if (o instanceof Throwable) {
                o = ExceptionUtils.getStackTrace((Throwable) o);
            }
            return o == null ? "(null)" : o.toString();
        }

        @Override
        public void send(Object o) {

            if (getSession() == null || ((ModelingEngine) KLAB.ENGINE).getNotificationBus() == null) {
                KLAB.info(o);
            } else {

                Notification n = getNotification();
                n.defineLevel(Level.INFO);

                if (o instanceof Throwable) {
                    n.setBody(ExceptionUtils.getFullStackTrace((Throwable) o));
                    n.setExceptionClass(o.getClass().getCanonicalName());
                    n.defineLevel(Level.SEVERE);
                } else if (o instanceof ITask) {
                    n.setTask(KLAB.MFACTORY.adapt(o, Task.class));
                    n.setTaskId(((ITask) o).getTaskId());
                    n.setContextId(((ITask) o).getContext().getId());
                    n.setSessionId(((ITask) o).getContext().getSession().getId());
                } else if (o instanceof IContext) {
                    n.setContext(KLAB.MFACTORY.adapt(o, org.integratedmodelling.common.beans.Context.class));
                    n.setContextId(((IContext) o).getId());
                    n.setSessionId(((IContext) o).getSession().getId());
                } else {

                    if (o instanceof String) {
                        /*
                         * if we're sending a task message and the context has changed,
                         * send it along. If the context changes, it's always because of a
                         * task, so this will only happen here.
                         */
                        if (o.toString().startsWith(Messages.TASK_MESSAGE_PREFIX)) {
                            if (((Context) getContext()).hasDeltas()) {
                                n.setContext(KLAB.MFACTORY
                                        .adapt(getContext(), org.integratedmodelling.common.beans.Context.class));
                            }
                            if (o.toString().equals(Messages.TIME_TRANSITION)) {
                                /*
                                 * pass along the task with the transition
                                 */
                                n.setTask(KLAB.MFACTORY.adapt(getTask(), Task.class));
                                n.setTaskId(getTask().getTaskId());
                                n.setContextId(getContext().getId());
                                n.setSessionId(getContext().getSession().getId());
                            }
                        }
                        n.setNotificationClass(o.toString());

                    } else if (o instanceof IModelBean) {

                        /*
                         * wrap the bean into a notification to be pushed to receive() at
                         * the receiving end.
                         */
                        n.defineLevel(Level.CONFIG);
                        n.setBody(((ModelingEngine) KLAB.ENGINE).advertiser.toJSON((IModelBean) o));
                        n.setNotificationClass(o.getClass().getCanonicalName());
                    }
                }

                ((ModelingEngine) KLAB.ENGINE).getNotificationBus().send(n, getChannel());
            }
        }

        public void setContext(Context context) {
            this.context = context;
        }
    }

    @Override
    public ISession createSession(IUser user) throws KlabException {
        return new EngineSession(user);
    }

    @Override
    public IUser getUser() {
        return ((EngineNetwork) network).getUser();
    }

    @Override
    public boolean provides(Object id) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void addListener(Listener listener) {
        listeners.add(listener);
    }

    @Override
    public String getCommunicationChannel() {
        return communicationChannel;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy