/*** * Copyright (c) 1995-2010 Cycorp R.E.R. d.o.o * * 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. */ package eu.larkc.core.pluginregistry; import static com.cyc.tool.subl.jrtl.nativeCode.subLisp.ConsesLow.list; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.openrdf.model.URI; import org.openrdf.model.impl.URIImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import com.cyc.tool.subl.jrtl.nativeCode.subLisp.Errors; import com.cyc.tool.subl.jrtl.nativeCode.subLisp.SubLThread; import com.cyc.tool.subl.jrtl.nativeCode.type.core.SubLConsPair; import com.cyc.tool.subl.jrtl.nativeCode.type.core.SubLObject; import com.cyc.tool.subl.jrtl.nativeCode.type.core.SubLObjectFactory; import com.cyc.tool.subl.jrtl.nativeCode.type.core.SubLString; import eu.larkc.core.Larkc; import eu.larkc.core.LarkcKBStatus; import eu.larkc.core.pluginregistry.query.SparqlQuery; import eu.larkc.core.pluginregistry.query.exceptions.MalformedSparqlQueryException; import eu.larkc.core.pluginregistry.query.exceptions.SparqlQueryTransformException; import eu.larkc.plugin.Plugin; import eu.larkc.shared.Resources; /** * The LarKC platform plug-in registry. This class stores all the plug-in meta * data and is able to execute queries agaisnt the plug-ins KB * * @author Blaz Fortuna, Luka Bradesko * */ public class PluginRegistry { private static Logger logger = LoggerFactory .getLogger(PluginRegistry.class); private String PLUGIN_DIR = "." + File.separatorChar + "plugins"; // count for registered plug-ins. Used also to generate unique id in the // internal kb. static int iPluginCount = 0; private final HashMap<URI, Class<?>> javaPluginClassH; // implementation of SubL Thread used to execute commands // against Cyc reasoning engine and internal KB abstract class SublThreadLink extends SubLThread { private boolean executedOk; public SubLObject result; public String sresult; public SublThreadLink() { super(null, "Plug-in registry"); executedOk = false; } public SubLObject askQuery(String sQuery) { return CycUtil.askQuery(sQuery, false); } /** * actual execution against Cyc RE and KB happens here */ public abstract void runImpl(); @Override public void run() { try { runImpl(); } catch (Exception e) { e.printStackTrace(); synchronized (this) { executedOk = false; } } finally { synchronized (this) { executedOk = true; } } } /** * Checkes if the query has successfully executed * * @return true if query successfully executed */ public synchronized boolean isExecutedOk() { return executedOk; } } /** * Initializes the registry and populates its KB (ex Cyc KB). * * @param pluginsPath * the directory where plug-ins are stores. If null default will * be taken */ public PluginRegistry(String pluginsPath) { if (pluginsPath != null && pluginsPath.length() != 0) { // check if path exists File file = new File(PLUGIN_DIR); if (file.isDirectory() && file.getAbsoluteFile().exists()) PLUGIN_DIR = pluginsPath; else logger.warn(pluginsPath + " is not a valid directory. Default ./plugins will be used for plug-ins import!"); } if (Larkc.getKBStatus() == LarkcKBStatus.NOT_INITIALIZED) initializeLarkcKb(); javaPluginClassH = new HashMap<URI, Class<?>>(); } /** * Initializes the registry and populates its KB (ex Cyc KB). */ public PluginRegistry() { this(null); } /** * Instantiates the plug-in * * @param pluginUri * @return plug-in instance * @throws InstantiationException * @throws IllegalAccessException * @throws InvocationTargetException * @throws IllegalArgumentException */ public Plugin getNewPluginInstance(URI pluginUri) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class<?> plugin = javaPluginClassH.get(pluginUri); if (plugin == null) return null; Constructor<?>[] constructors = plugin.getDeclaredConstructors(); for (Constructor<?> constructor : constructors) { if (constructor.getGenericParameterTypes().length == 1) { return (Plugin) constructor .newInstance(new Object[] { pluginUri }); } } throw new InstantiationException("Cannot instantiate the " + pluginUri.getLocalName() + " plug-in. It doesn't have (URI _plugInName) constructor!"); } /** * Initialize the core concepts of the Larkc upper-level ontology. All the * concepts and assertions are read from the rdf file, wrtten in Turtle * format. */ private void initializeLarkcKb() { // read larkc ontology // InputStream fstream = ClassLoader.getSystemClassLoader() // .getResourceAsStream(LARKC_RDF); Larkc.setKBStatus(LarkcKBStatus.INITIALIZING); InputStream fstream = this.getClass().getClassLoader() .getResourceAsStream(Resources.LARKC_RDF); try { if (fstream == null) { logger.error("Cannot find the " + Resources.LARKC_RDF + " file. PLUGIN REGISTRY MIGHT NOT WORK!!"); Larkc.setKBStatus(LarkcKBStatus.NOT_INITIALIZED); return; } CycUtil.loadRdfTurtle(fstream); } catch (Exception e) { logger.error("Error parsing the " + Resources.LARKC_RDF + ". PLUGIN REGISTRY MIGHT NOT WORK!! " + e.getMessage()); Larkc.setKBStatus(LarkcKBStatus.NOT_INITIALIZED); } // add a rule used for inferring connections between plug-ins String mtStr = "BaseKB"; String forwardRuleStr = "(#$implies " + " (#$and " + " (#$genls ?X #$larkc-Plugin) " + " (#$genls ?Y #$larkc-Plugin) " + " (#$larkc-hasOutputType ?X ?TYPE) " + " (#$larkc-hasInputType ?Y ?TYPE1) " + " (#$genls ?TYPE ?TYPE1))" // + " (#$isa ?Z ?X)" // + " (#$isa ?V ?Y))" + " (#$larkc-pluginByDataConnectsTo ?X ?Y))"; CycUtil.addForwardRule(forwardRuleStr, mtStr); Larkc.setKBStatus(LarkcKBStatus.INITIALIZED); } /** * Load plug-ins from the ini file and from PLATFORM/plugins or specified * dir (over runtime params). */ public void loadPlugins() { // checks files and directories in PLATFORM/plugins File pluginsDir = new File(PLUGIN_DIR); File[] pluginFiles = pluginsDir.listFiles(); if (pluginFiles != null && pluginFiles.length != 0) { for (File file : pluginFiles) { findPlugins(file); } } else logger.warn("No plug-ins in the plugins directory in the " + PLUGIN_DIR + ". Using only plugins.ini"); try { // Open the plugins.ini where the additional plug-in list is written InputStream fstream = this.getClass().getClassLoader() .getResourceAsStream(Resources.PLUGINS_INI); if (fstream == null) { throw new RuntimeException(Resources.PLUGINS_INI + " not found. Plug-in registry will not work."); } DataInputStream in = new DataInputStream(fstream); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String strLine; // Read File Line By Line while ((strLine = br.readLine()) != null) { if (strLine.startsWith("//") || strLine.trim().isEmpty()) continue; if (strLine.endsWith(".wsdl") || strLine.endsWith(".larkc")) { strLine.replace('/', File.separatorChar); strLine.replace('\\', File.separatorChar); findPlugins(new File(strLine)); } else logger.warn("Invalid line in the " + Resources.PLUGINS_INI + " file:" + strLine); }// read plugins.ini in.close(); } catch (IOException e) { logger.error("Error reading the " + Resources.PLUGINS_INI + " file:" + e.getMessage()); Errors.handleError(e); } } /** * Returns the URIs of all the plug-ins available to the platform * * @return Collection of Plugin URIs * @throws PluginRegistryQueryException */ public Collection<URI> getAllPlugins() throws PluginRegistryQueryException { return getPluginTypes("larkc-Plugin"); } /** * Returns the URIs of all the Decider plug-ins available to the platform * * @return Collection of Decider Plugin URIs * @throws PluginRegistryQueryException */ public Collection<URI> getAllDeciders() throws PluginRegistryQueryException { return getPluginTypes("larkc-Decider"); } /** * Returns the URIs of all the Identifier plug-ins available to the platform * * @return Collection of Identifier Plugin URIs * @throws PluginRegistryQueryException */ public Collection<URI> getAllIdentifiers() throws PluginRegistryQueryException { return getPluginTypes("larkc-Identifier"); } /** * Returns the URIs of all the Transformer plug-ins available to the * platform * * @return Collection of Transformer Plugin URIs * @throws PluginRegistryQueryException */ public Collection<URI> getAllTransformers() throws PluginRegistryQueryException { return getPluginTypes("larkc-InformationSetTransformer"); } /** * Returns the URIs of all the Selecter plug-ins available to the platform * * @return Collection of Selecter Plugin URIs * @throws PluginRegistryQueryException */ public Collection<URI> getAllSelecters() throws PluginRegistryQueryException { return getPluginTypes("larkc-Selecter"); } /** * Returns the URIs of all the Reasoner plug-ins available to the platform * * @return Collection of Reasoner Plugin URIs * @throws PluginRegistryQueryException */ public Collection<URI> getAllReasoners() throws PluginRegistryQueryException { return getPluginTypes("larkc-Reasoner"); } /** * Returns true if _plugin is a LarKC plugin * * @param _plugin * * @return Collection of Plugin URIs * @throws PluginRegistryQueryException */ public boolean isLarkCPlugin(URI _plugin) throws PluginRegistryQueryException { String cycQuery = "(#$thereExists ?X " + " (#$and " + " (#$larkc-hasUri ?X \"" + _plugin + "\")" + " (#$larkc-hasUri ?X ?Y)))"; /* * String cycQuery = "(#$thereExists ?X " + " (#$and " + * " (#$genls #$larkc-Plugin ?X)" + " (#$larkc-hasUri ?X \"" + _plugin * + "\")))"; */ return !getPluginCyc(cycQuery).isEmpty(); } /** * Returns the Endpoint (how to access it) of the given plug-ins * * @param _Plugin * URI of the plug-in * @return Endpoint location * @throws PluginRegistryQueryException */ public String getPluginEndpoint(final URI _Plugin) throws PluginRegistryQueryException { class QueryThread extends SublThreadLink { String endpoint = null; @Override public void runImpl() { String name = _Plugin.stringValue(); String query = "(#$thereExists ?X " + " (#$and " + " (#$larkc-hasUri ?X \"" + name + "\")" + " (#$larkc-hasEndpoint ?X ?Y)))"; SubLObject result = super.askQuery(query); SubLConsPair SubLendpoint = (SubLConsPair) result.first() .first(); endpoint = SubLendpoint.getDottedElement().toString(); } public String getEndpoint() { return endpoint; } } QueryThread qt = new QueryThread(); // start the query thread qt.start(); // wait for the query to finish try { qt.join(); } catch (InterruptedException e) { throw new PluginRegistryQueryException(); } // check all ok executed if (!qt.isExecutedOk()) { throw new PluginRegistryQueryException(); } // all fine, return the plug-in endpoint return qt.getEndpoint(); } /** * Returns a collection of all the plug-ins of the type sPluginTypeConcept * * @param sPluginTypeConcept * @return collection of plug-in URIs * @throws PluginRegistryQueryException */ private Collection<URI> getPluginTypes(final String sPluginTypeConcept) throws PluginRegistryQueryException { String cycQuery = "(#$thereExists ?X " + " (#$and " + " (#$genls ?X #$" + sPluginTypeConcept + ")" + " (#$larkc-hasUri ?X ?URN)))"; return getPluginCyc(cycQuery); } /** * Returns a collection of all the plug-ins corresponding to cycQuery * * @param sPluginTypeConcept * @return collection of plug-in URIs * @throws PluginRegistryQueryException */ private Collection<URI> getPluginCyc(final String cycQuery) throws PluginRegistryQueryException { // prepare cyc query thread link class QueryThread extends SublThreadLink { private Collection<URI> plugins = new ArrayList<URI>(); @Override public void runImpl() { SubLObject result = super.askQuery(cycQuery); for (int i = 0; i < result.size(); i++) { if (result.get(i).isNil()) continue; SubLConsPair urn = (SubLConsPair) result.get(i).first(); String stringy = urn.getDottedElement().toString() .replaceAll("\"", ""); plugins.add(new URIImpl(stringy)); } } public Collection<URI> getAllPlugins() { return plugins; } } QueryThread qt = new QueryThread(); // start the query thread qt.start(); // wait for the query to finish try { qt.join(); } catch (InterruptedException e) { throw new PluginRegistryQueryException(); } // check all ok executed if (!qt.isExecutedOk()) { throw new PluginRegistryQueryException(); } // all fine, return the list of plug-ins return qt.getAllPlugins(); } /** * Returns a collection of all the plug-ins that correspond to the specified * input SPARQL query * * @param sparqlQuery * @return collection of plug-in URIs * @throws MalformedSparqlQueryException * @throws SparqlQueryTransformException * @throws PluginRegistryQueryException */ public Collection<URI> getPluginSparql(final String sparqlQuery) throws MalformedSparqlQueryException, SparqlQueryTransformException, PluginRegistryQueryException { // translate sparql query to cyc query SparqlQuery query = new SparqlQuery(sparqlQuery); final String cycQuery = query.toCycQuery(); return getPluginCyc(cycQuery); } /** * Registers the plug-in or more plug-ins in the directory, but only one * level deep * * @param fileOrDir * directory or a file location of the plug-in */ private void findPlugins(File fileOrDir) { InputStream wsdlFile = null; File wsdlFileParent = null; if (fileOrDir.isDirectory()) { for (File file : fileOrDir.listFiles()) { if (!file.isDirectory())// only check directories one level deep findPlugins(file); }// list files in the sub-directory return;// after scanned all the files it already registered whatever // it had. So the method should end. } else { String fileOrDirName = fileOrDir.getName(); if (fileOrDirName.endsWith(".wsdl")) { try { wsdlFile = new FileInputStream(fileOrDir); } catch (FileNotFoundException e) { logger.warn("File doesn't exists: " + fileOrDir.getAbsolutePath()); return; } wsdlFileParent = fileOrDir.getParentFile(); } else if (fileOrDirName.endsWith(".larkc") || fileOrDirName.endsWith(".jar")) { try { String unzipDirName = fileOrDirName.substring(0, fileOrDirName.lastIndexOf('.')); File unzipWhere = new File(fileOrDir.getParentFile() .getAbsolutePath() + File.separator + unzipDirName); // if the directory exists already, unzip anyway since the // plug-in might have been updated if (unzipWhere.exists()) { logger.debug( "Plug-in {} was already registered. Redeploying plug-in...", unzipDirName); } unzip(fileOrDir, unzipDirName); findPlugins(unzipWhere); return; } catch (ZipException e) { logger.warn( "Cannot extract " + fileOrDir.getAbsolutePath(), e); } catch (IOException e) { logger.warn( "Cannot extract " + fileOrDir.getAbsolutePath(), e); } } else // ignore other files return; } Document document; try { DocumentBuilder documentBuilder = DocumentBuilderFactory .newInstance().newDocumentBuilder(); document = documentBuilder.parse(wsdlFile); } catch (Exception e) { logger.warn("Error parsing the wsdl file " + wsdlFile + " in:" + fileOrDir.getAbsolutePath(), e); return; } NodeList plugins = document.getElementsByTagName("wsdl:service"); for (int iPluginNum = 0; iPluginNum < plugins.getLength(); iPluginNum++) { Element plugin = (Element) plugins.item(iPluginNum); String sPluginName = plugin.getAttribute("name"); String sRdfReferece = plugin.getAttribute("sawsdl:modelReference"); String sRdfFile = sRdfReferece.split("#")[0]; InputStream rdfFileStream = findFileInJarOrDir(wsdlFileParent, ".rdf"); if (rdfFileStream == null) { logger.warn("Cannot find the rdf file in " + wsdlFileParent.getAbsolutePath() + ". Checking if old (obsolete) plug-in model is used... "); File oldRdfLocation = new File(wsdlFileParent.getAbsolutePath() + File.separator + sRdfFile); try { rdfFileStream = new FileInputStream(oldRdfLocation); } catch (FileNotFoundException e) { logger.warn("Cannot find the rdf file in " + oldRdfLocation.getAbsolutePath() + ". Skipping this plug-in registration "); return; } } NodeList endpoints = plugin.getElementsByTagName("wsdl:endpoint"); String sLocation = ((Element) endpoints.item(0)) .getAttribute("location"); if (sLocation.startsWith("java:")) { loadPluginClass(new URIImpl(sPluginName), sLocation.substring(5), wsdlFileParent); logger.info("Registered the " + sLocation.substring(5)); } else { logger.warn("Other endpoints than java: are currently not supported (" + sLocation + ")"); } try { CycUtil.loadRdfTurtle(rdfFileStream); SubLObject sblPlugin = CycUtil.addRdfTerm(sRdfReferece); SubLObject hasUri = CycUtil .addRdfTerm("http://larkc.eu/plugin#hasUri"); SubLString uri = SubLObjectFactory.makeString(sPluginName); SubLObject cycAssertion = list(hasUri, sblPlugin, uri); CycUtil.addAssertion(cycAssertion, CycUtil.mtStr); SubLObject hasEndpoint = CycUtil .addRdfTerm("http://larkc.eu/plugin#hasEndpoint"); SubLString endpoint = SubLObjectFactory.makeString(sLocation); cycAssertion = list(hasEndpoint, sblPlugin, endpoint); CycUtil.addAssertion(cycAssertion, CycUtil.mtStr); } catch (Exception e) { logger.warn("Error parsing the " + sRdfFile + "(" + e.getMessage() + ")"); } }// for all services in wsdl } /** * Lists the files from given jar file or directory and returns the first * occurrence of .wsdl file * * @param _theJar * @return tha InputStream of the wsdl file */ private InputStream findFileInJarOrDir(File _theJar, String _suffix) { try { if (_theJar.isDirectory()) { for (File file : _theJar.listFiles()) { if (!file.isDirectory() && file.getName().endsWith(_suffix)) { return new FileInputStream(file); } }// list files in the subdirectory } else { JarFile jarFile; jarFile = new JarFile(_theJar); Enumeration<JarEntry> enumr = jarFile.entries(); while (enumr.hasMoreElements()) { JarEntry entry = (JarEntry) enumr.nextElement(); if (entry.getName().endsWith(_suffix)) { URLClassLoader cl = URLClassLoader .newInstance(new URL[] { _theJar.toURI() .toURL() }); InputStream is = cl .getResourceAsStream(entry.getName()); return is; } } } } catch (IOException e) { logger.warn("Error reading from the " + _theJar.getAbsolutePath()); } return null; } /** * Finds the class file and loads it with ClassLoader * * @param _class * name with package included * @param pluginPath * path to the root of where the class is */ @SuppressWarnings("unchecked") private void loadPluginClass(URI _pluginName, String _class, File file) { try { // find external plug-in libraries ArrayList<URL> vUrl = new ArrayList<URL>(); File libDir = new File(file.getCanonicalPath() + File.separator + "lib"); if (libDir.exists()) { for (File jarlib : libDir.listFiles()) { if (jarlib.getName().endsWith(".jar")) { vUrl.add(jarlib.toURI().toURL()); } }// list files in the subdirectory } else { logger.warn("Can not find the " + libDir.getCanonicalPath() + ". Assuming this plug-in doesn't have any libraries"); } URL url = file.toURI().toURL(); vUrl.add(url); URL[] urls = new URL[vUrl.size()]; for (int i = 0; i < vUrl.size(); i++) { urls[i] = vUrl.get(i); } // Create a new class loader with the directory ClassLoader classLoader = new URLClassLoader(urls); // load the class Class<Plugin> pluginClass = (Class<Plugin>) classLoader .loadClass(_class); javaPluginClassH.put(_pluginName, pluginClass); } catch (MalformedURLException e) { Errors.handleError(e); } catch (ClassNotFoundException e) { logger.warn("Classloader cannot find class: " + e.getMessage() + "! Plug-in not loaded!"); } catch (ClassCastException e) { Errors.handleError("Plugin \"" + _class + "\" must implement" + Plugin.class.getName() + " interface", e); } catch (IOException e) { logger.error("Error loading " + _class + "! " + e.getMessage()); } catch (IncompatibleClassChangeError e) { logger.warn("Classloader cannot load plug-in class: " + _class + "! The plug-in API had changed. Pleas recompile the plug-in. Plug-in not loaded!"); } } /* * private void setDecider(Class<Plugin> theDecider) { this.defaultDecider = * theDecider; } */ /** * Unzips the .larkc or other file into unzipSubDir directory */ private void unzip(File file, String unzipSubDir) throws ZipException, IOException { int BUFFER = 2048; BufferedOutputStream dest = null; BufferedInputStream is = null; ZipEntry entry; ZipFile zipfile = new ZipFile(file); Enumeration<? extends ZipEntry> e = zipfile.entries(); while (e.hasMoreElements()) { entry = (ZipEntry) e.nextElement(); is = new BufferedInputStream(zipfile.getInputStream(entry)); int count; byte data[] = new byte[BUFFER]; File where = new File(file.getParentFile().getAbsolutePath() + File.separator + unzipSubDir + File.separator + entry.getName()); if (entry.isDirectory()) { where.mkdirs(); continue; } else { where.getParentFile().mkdirs(); where.createNewFile(); } FileOutputStream fos = new FileOutputStream( where.getCanonicalFile()); dest = new BufferedOutputStream(fos, BUFFER); while ((count = is.read(data, 0, BUFFER)) != -1) { dest.write(data, 0, count); } dest.flush(); dest.close(); is.close(); } } }