org.integratedmodelling.engine.Engine 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.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.jcs.JCS;
import org.integratedmodelling.Version;
import org.integratedmodelling.api.annotations.ResourceService;
import org.integratedmodelling.api.auth.IUser;
import org.integratedmodelling.api.components.Component;
import org.integratedmodelling.api.configuration.IConfiguration;
import org.integratedmodelling.api.configuration.IResourceConfiguration;
import org.integratedmodelling.api.engine.IEngine;
import org.integratedmodelling.api.engine.ILock;
import org.integratedmodelling.api.knowledge.IConcept;
import org.integratedmodelling.api.knowledge.IProperty;
import org.integratedmodelling.api.metadata.IModelMetadata;
import org.integratedmodelling.api.metadata.IObservationMetadata;
import org.integratedmodelling.api.modelling.IDirectObserver;
import org.integratedmodelling.api.modelling.IObservableSemantics;
import org.integratedmodelling.api.modelling.resolution.IResolutionScope;
import org.integratedmodelling.api.monitoring.IProjectLifecycleListener;
import org.integratedmodelling.api.network.IComponent;
import org.integratedmodelling.api.network.INetwork;
import org.integratedmodelling.api.network.INode;
import org.integratedmodelling.api.project.IProject;
import org.integratedmodelling.api.runtime.ITask;
import org.integratedmodelling.api.services.IPrototype;
import org.integratedmodelling.api.services.annotations.CLIPrototype;
import org.integratedmodelling.api.services.annotations.Prototype;
import org.integratedmodelling.collections.Pair;
import org.integratedmodelling.common.beans.ObservationData;
import org.integratedmodelling.common.beans.responses.Capabilities;
import org.integratedmodelling.common.command.ServiceManager;
import org.integratedmodelling.common.configuration.Configuration;
import org.integratedmodelling.common.configuration.KLAB;
import org.integratedmodelling.common.interfaces.DirectResourceService;
import org.integratedmodelling.common.kim.KIMModelManager;
import org.integratedmodelling.common.network.ResourceConfiguration;
import org.integratedmodelling.common.project.ProjectManager;
import org.integratedmodelling.common.resources.ResourceFactory;
import org.integratedmodelling.common.utils.FileUtils;
import org.integratedmodelling.common.utils.URLUtils;
import org.integratedmodelling.common.vocabulary.NS;
import org.integratedmodelling.common.vocabulary.ObservationMetadata;
import org.integratedmodelling.engine.geospace.coverage.vector.VectorCoverage;
import org.integratedmodelling.engine.modelling.kbox.ObservationKbox;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabIOException;
import org.integratedmodelling.exceptions.KlabUnsupportedOperationException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.filter.AnnotationTypeFilter;
public abstract class Engine implements IEngine {
public static IConcept DOUBLE;
public static IConcept BOOLEAN;
public static IConcept TEXT;
public static IConcept LONG;
public static IConcept INTEGER;
public static IConcept FLOAT;
public static IConcept NUMBER;
public static IConcept THING;
public static IConcept NOTHING;
public static IProperty CLASSIFICATION_PROPERTY;
public static IProperty ABSTRACT_PROPERTY;
protected long bootTime;
protected ILock lock;
private ResourceConfiguration resourceConfiguration;
protected INetwork network;
/**
* extract core knowledge to a directory. Should become unnecessary as we
* can use the /get/directory/im:ks:core.knowledge for the same purposes in
* regular network setups. Called only by the start() function in
* ModelingEngine, for programmatical use. The servers use their own version
* that integrates with the get resources service.
*/
public static void extractKnowledge() throws Exception {
File kdir = KLAB.CONFIG.getDataPath("knowledge");
File sdir = KLAB.CONFIG.getDataPath("ssh");
File rcon = new File(KLAB.CONFIG.getDataPath() + File.separator + "access.properties");
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("/knowledge/**");
Resource[] keyring = resolver.getResources("/ssh/pubring.gpg");
if (!rcon.exists()) {
URL prototype = null;
try {
prototype = resolver.getResource("classpath:access.properties.prototype").getURL();
URLUtils.copy(prototype, rcon);
} catch (IOException e) {
// just don't
}
}
if (keyring.length > 0) {
try (InputStream in = keyring[0].getURL().openStream()) {
FileUtils.copyInputStreamToFile(in, new File(sdir + File.separator + "pubring.gpg"));
} catch (Exception e) {
throw new KlabIOException(e);
}
}
for (Resource resource : resources) {
String path = null;
if (resource instanceof FileSystemResource) {
path = ((FileSystemResource) resource).getPath();
} else if (resource instanceof ClassPathResource) {
path = ((ClassPathResource) resource).getPath();
}
if (path == null) {
throw new KlabIOException("internal: cannot establish path for resource " + resource);
}
if (!path.endsWith("owl")) {
continue;
}
String filePath = path.substring(path.indexOf("knowledge/") + "knowledge/".length());
int pind = filePath.lastIndexOf('/');
if (pind >= 0) {
String fileDir = filePath.substring(0, pind);
File destDir = new File(kdir + File.separator + fileDir);
destDir.mkdirs();
}
File dest = new File(kdir + File.separator + filePath);
InputStream is = resource.getInputStream();
FileUtils.copyInputStreamToFile(is, dest);
is.close();
}
}
/*
* prevent BS warnings from various libs.
*/
static {
System.setProperty("com.sun.media.jai.disableMediaLib", "true");
}
public Engine() throws KlabException {
KLAB.ENGINE = this;
KLAB.CONFIG = new Configuration(false);
KLAB.PMANAGER = new ProjectManager();
KLAB.PMANAGER.addListener(new IProjectLifecycleListener() {
@Override
public void projectUnregistered(IProject project) {
}
@Override
public void projectRegistered(IProject project) {
}
@Override
public void projectPropertiesModified(IProject project, File file) {
}
@Override
public void onReload(boolean full) {
try {
ObservationKbox.get().reindexLocalObservations();
} catch (KlabException e) {
KLAB.warn("reindexing of observations failed: " + e.getCause().getMessage());
}
}
@Override
public void namespaceModified(String ns, IProject project) {
}
@Override
public void namespaceDeleted(String ns, IProject project) {
}
@Override
public void namespaceAdded(String ns, IProject project) {
}
@Override
public void fileModified(IProject project, File file) {
}
@Override
public void fileDeleted(IProject project, File file) {
}
@Override
public void fileCreated(IProject project, File file) {
}
});
/*
* needs to be defined for JCS config to work properly.
*/
if (System.getProperty(IConfiguration.KLAB_WORK_DIRECTORY) == null) {
System.setProperty(IConfiguration.KLAB_WORK_DIRECTORY, ".tl");
}
JCS.setConfigFilename("/org/integratedmodelling/cache.ccf");
KLAB.MMANAGER = new KIMModelManager();
KLAB.KM = new KnowledgeManager();
KLAB.MFACTORY = new ModelFactory();
}
/**
* Perform initial startup: creating configuration, setting up versions and
* essential concepts. Be sure to call super.startup() in any virtuals
* extending this.
*
* @throws KlabException
*/
protected void startup() throws KlabException {
String buildInfo = "";
if (!Version.VERSION_BUILD.equals("VERSION_BUILD")) {
buildInfo = " build " + Version.VERSION_BUILD + " (" + Version.VERSION_BRANCH + " " + Version.VERSION_DATE
+ ")";
}
String vdesc = new Version().toString() + buildInfo;
KLAB.info("Thinklab engine [" + vdesc + "] booting on " + new Date());
bootTime = new Date().getTime();
((KnowledgeManager) (KLAB.KM)).initialize();
INTEGER = KLAB.KM.getConcept(NS.INTEGER);
FLOAT = KLAB.KM.getConcept(NS.FLOAT);
TEXT = KLAB.KM.getConcept(NS.TEXT);
LONG = KLAB.KM.getConcept(NS.LONG);
DOUBLE = KLAB.KM.getConcept(NS.DOUBLE);
NUMBER = KLAB.KM.getConcept(NS.NUMBER);
BOOLEAN = KLAB.KM.getConcept(NS.BOOLEAN);
// CLASSIFICATION_PROPERTY = KLAB.KM.getProperty(NS.CLASSIFICATION_PROPERTY);
ABSTRACT_PROPERTY = KLAB.KM.getProperty(NS.IS_ABSTRACT);
registerCommonAnnotations();
}
public ClassLoader swapClassloader() {
ClassLoader clsl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
return clsl;
}
public void resetClassLoader(ClassLoader clsl) {
Thread.currentThread().setContextClassLoader(clsl);
}
@Override
public void shutdown(final int seconds) {
new Thread() {
@Override
public void run() {
int status = 0;
if (seconds > 0) {
try {
sleep(seconds * 1000);
} catch (InterruptedException e) {
status = 255;
}
}
System.exit(status);
}
}.start();
}
@Override
public List importObservations(File file) throws KlabException {
/*
* TODO when it's worth it, we may want to use a plug-in system with
* components to recognize extensions, as we used to have.
*/
if (file.toString().endsWith(".shp")) {
Collection features = VectorCoverage.readFeatures(file);
/*
* TODO
*/
}
throw new KlabUnsupportedOperationException("resource " + file + " cannot be imported: format not recognized");
}
@Override
public long getBootTime() {
return bootTime;
}
public static interface AnnotationHandler {
void processAnnotatedClass(Annotation annotation, Class cls) throws KlabException;
}
static Map, AnnotationHandler> annotationHandlers = new HashMap<>();
public static void registerAnnotation(Class annotationClass, AnnotationHandler handler) {
annotationHandlers.put(annotationClass, handler);
}
/**
* Single scanning loop for all registered annotations in a package. Done on
* the main codebase and in each component based on the declared packages.
*
* @param packageId
* @throws KlabException
*/
@Override
public List>> scanPackage(String packageId) throws KlabException {
List>> ret = new ArrayList<>();
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
for (Class ah : annotationHandlers.keySet()) {
provider.addIncludeFilter(new AnnotationTypeFilter(ah));
}
Set beans = provider.findCandidateComponents(packageId);
for (BeanDefinition bd : beans) {
for (Class ah : annotationHandlers.keySet()) {
try {
Class cls = Class.forName(bd.getBeanClassName());
Annotation annotation = cls.getAnnotation(ah);
if (annotation != null) {
annotationHandlers.get(ah).processAnnotatedClass(annotation, cls);
ret.add(new Pair<>(annotation, cls));
}
} catch (ClassNotFoundException e) {
KLAB.error(e);
continue;
}
}
}
return ret;
}
protected void registerCommonAnnotations() {
registerAnnotation(ResourceService.class, new AnnotationHandler() {
@SuppressWarnings("unchecked")
@Override
public void processAnnotatedClass(Annotation annotation, Class cls) {
if (org.integratedmodelling.common.interfaces.ResourceService.class.isAssignableFrom(cls)) {
ResourceFactory.serviceRegistry.put(((ResourceService) annotation).service(),
(Class) cls);
}
}
});
registerAnnotation(Prototype.class, new AnnotationHandler() {
@Override
public void processAnnotatedClass(Annotation annotation, Class cls) {
if (cls.getAnnotation(CLIPrototype.class) == null) {
ServiceManager.get().processPrototypeDeclaration((Prototype) annotation, cls);
}
}
});
registerAnnotation(Component.class, new AnnotationHandler() {
@Override
public void processAnnotatedClass(Annotation annotation, Class cls) throws KlabException {
// do nothing but do record the class and its occurrence.
// if (KLAB.CMANAGER.getComponent(((Component) annotation).id())
// == null)
// {
// KLAB.CMANAGER.register((Component) annotation, cls);
//
// /*
// * in personal engines, no need to keep reallocating
// connections in what
// can
// * potentially be a slow operation.
// */
// if (Engine.this instanceof ModelingEngine) {
// ModelKbox.get().getDatabase().preallocateConnection();
// ObservationKbox.get().getDatabase().preallocateConnection();
// }
//
// KLAB.CMANAGER.link();
//
// /*
// * in personal engines, no need to keep reallocating
// connections in what
// can
// * potentially be a slow operation.
// */
// if (Engine.this instanceof ModelingEngine) {
// ModelKbox.get().getDatabase().deallocateConnection();
// ObservationKbox.get().getDatabase().deallocateConnection();
// }
// }
}
});
}
@Override
public String getName() {
return KLAB.NAME;
}
@Override
public String getUrl() {
return network.getUrl();
}
@Override
public IDirectObserver retrieveObservation(String observationid, String nodeId) throws KlabException {
IDirectObserver ret = null;
ObservationMetadata omd = ObservationKbox.get().retrieveByName(observationid);
if (omd != null) {
return omd.getSubjectObserver(getMonitor());
}
/*
* broadcast call for search function on network.
*/
if (Engine.this instanceof ModelingEngine) {
if (nodeId != null) {
ret = network.getNode(nodeId).retrieveObservation(observationid, nodeId);
} else {
// ServiceCall scl = ServiceManager
// .getServiceCall(Endpoints.RETRIEVE_OBSERVATION,
// "observation-name",
// observationid);
// Object mdd = ((Network)KLAB.NETWORK).broadcast(scl.post(),
// scl.getMonitor());
// if (mdd instanceof Collection) {
// for (Object md : ((Collection) mdd)) {
// if (md instanceof ObservationMetadata) {
// omd = (ObservationMetadata) md;
// break;
// }
// }
// }
}
}
if (omd != null && ret == null) {
ret = omd.getSubjectObserver(getMonitor());
}
return ret;
}
@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 Collection getFunctions() {
// return null;
// }
//
// @Override
// public IPrototype getFunctionPrototype(String id) {
// return KLAB.MMANAGER.getFunctionPrototype(id);
// }
/**
* Put all the capabilities accessible to a given user into the passed bean.
*
* @param user
* @param bean
*/
public void getCapabilities(IUser user, Capabilities bean) {
}
@Override
public IResourceConfiguration getResourceConfiguration() {
if (resourceConfiguration == null) {
File f = new File(KLAB.CONFIG.getDataPath() + File.pathSeparator + "access.properties");
URL src = null;
if (f.exists()) {
try {
src = f.toURI().toURL();
} catch (MalformedURLException e) {
// screw it
}
}
resourceConfiguration = new ResourceConfiguration(src);
}
return resourceConfiguration;
}
@Override
public IPrototype getFunctionPrototype(String id) {
IPrototype ret = ServiceManager.get().getFunctionPrototype(id);
if (ret != null) {
return ret;
}
/*
* look it up in the network
*/
for (INode node : getNetwork().getNodes()) {
ret = node.getFunctionPrototype(id);
if (ret != null) {
return ret;
}
}
return null;
}
@Override
public Collection getFunctionPrototypes() {
return ServiceManager.get().getFunctionPrototypes();
}
/**
* Complete the boot phase by loading the knowledge. In node servers, this
* must be done after all REST services are initialized, as the URN in
* projects may refer to the node itself, and their resolution requires the
* /get service to be available. So we call startup() first, then initialize
* REST (Spring application), then call loadKnowledge().
*
* @throws KlabException
*/
public final void loadKnowledge() throws KlabException {
KLAB.info("deploying components");
((ProjectManager) KLAB.PMANAGER).deployComponents();
KLAB.info("loading knowledge");
for (String sproject : getResourceConfiguration().getSharedProjectIds()) {
KLAB.PMANAGER.loadProject(sproject, KLAB.MFACTORY.getRootParsingContext());
}
KLAB.info("loading component knowledge");
for (IComponent component : KLAB.PMANAGER.getComponents()) {
KLAB.PMANAGER.loadComponent(component, KLAB.MFACTORY.getRootParsingContext());
}
KLAB.info("project and component initialization completed");
}
@Override
public INetwork getNetwork() {
return network;
}
}