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;
}
}