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

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy