From 0e27cfcc00243f50cc62e1cff1b05ae6c40086f6 Mon Sep 17 00:00:00 2001 From: Julianus Pfeuffer Date: Fri, 29 Sep 2017 17:30:47 +0200 Subject: [PATCH 1/2] Changed implementation of the DynNodeSetFactories to register an ExtPoint where the different VersionedNodeSetFactories (from the versioned plugins) register and add the nodes. --- com.genericworkflownodes.knime/plugin.xml | 8 ++ ...knime.dynamic.VersionedNodeSetFactory.exsd | 109 ++++++++++++++ .../knime/custom/config/BinaryManager.java | 51 ++++++- .../custom/config/IPluginConfiguration.java | 7 + .../config/impl/PluginConfiguration.java | 32 ++++- .../dynamic/DynamicGenericNodeFactory.java | 25 ++-- .../dynamic/DynamicGenericNodeSetFactory.java | 42 +++--- .../dynamic/VersionedNodeSetFactory.java | 70 +++++++++ .../VersionedNodeSetFactoryManager.java | 134 ++++++++++++++++++ 9 files changed, 445 insertions(+), 33 deletions(-) create mode 100644 com.genericworkflownodes.knime/schema/com.genericworkflownodes.knime.dynamic.VersionedNodeSetFactory.exsd create mode 100644 com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactory.java create mode 100644 com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactoryManager.java diff --git a/com.genericworkflownodes.knime/plugin.xml b/com.genericworkflownodes.knime/plugin.xml index b9d2cd17..2919b7a7 100644 --- a/com.genericworkflownodes.knime/plugin.xml +++ b/com.genericworkflownodes.knime/plugin.xml @@ -6,6 +6,7 @@ + @@ -142,4 +143,11 @@ + + + + diff --git a/com.genericworkflownodes.knime/schema/com.genericworkflownodes.knime.dynamic.VersionedNodeSetFactory.exsd b/com.genericworkflownodes.knime/schema/com.genericworkflownodes.knime.dynamic.VersionedNodeSetFactory.exsd new file mode 100644 index 00000000..3f7013d2 --- /dev/null +++ b/com.genericworkflownodes.knime/schema/com.genericworkflownodes.knime.dynamic.VersionedNodeSetFactory.exsd @@ -0,0 +1,109 @@ + + + + + + + + + [Enter description of this extension point.] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Semantic versioning of the registered NodeSetFactory + + + + + + + + + + + + [Enter the first release in which this extension point appears.] + + + + + + + + + [Enter extension point usage example here.] + + + + + + + + + [Enter API information here.] + + + + + + + + + [Enter information about supplied implementation of this extension point.] + + + + + diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/BinaryManager.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/BinaryManager.java index 3fbf01cb..902adb0f 100644 --- a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/BinaryManager.java +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/BinaryManager.java @@ -20,7 +20,12 @@ import java.io.File; import java.io.IOException; +import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @@ -47,7 +52,12 @@ public final class BinaryManager { /** * Path inside the bundle where the binaries should be located. */ - private static final String BUNDLE_PATH = "payload"; + private static final String BUNDLE_PATH = "payload"; + + /** + * Path inside the bundle where the descriptors should be located. + */ + private static final String DESCRIPTORS_PATH = BUNDLE_PATH + File.separator + "descriptors"; /** * File that should be present to identify the correct path. @@ -162,6 +172,16 @@ private File findShippedBinary(final String executableName) { return null; } } + + public File resolveToolDescriptorPath(final String relToolPath) { + Bundle bundle = FrameworkUtil.getBundle(classInBundle); + try { + return new File(FileLocator.toFileURL(bundle.getResource(DESCRIPTORS_PATH + File.separator + relToolPath)).getFile()); + } catch (IOException e) { + // TODO Auto-generated catch block + return null; + } + } /** * Search the bundle for the given file name. @@ -188,4 +208,33 @@ private File findFileInBundle(final String fileName) { } } } + + /** + * Search the bundle for CTDs and list them in a List of Files. + * + * @return List of CTD Files in the bundle + * @throws URISyntaxException + */ + public Iterable listTools() { + Bundle bundle = FrameworkUtil.getBundle(classInBundle); + Enumeration e = bundle.findEntries(DESCRIPTORS_PATH, "*.ctd", true); + ArrayList files = new ArrayList<>(); + Path p; + try { + p = Paths.get(FileLocator.toFileURL(bundle.getResource(DESCRIPTORS_PATH)).toURI()); + } catch (Exception ex) { + return Collections.emptyList(); + } + while (e.hasMoreElements()){ + try { + Path el = Paths.get(FileLocator.toFileURL(e.nextElement()).toURI()); + files.add(p.relativize(el).toString()); + } catch (URISyntaxException e1) { + throw new AssertionError("Should not happen", e1); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + return files; + } } diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/IPluginConfiguration.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/IPluginConfiguration.java index 5ea32d34..dc89c77f 100644 --- a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/IPluginConfiguration.java +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/IPluginConfiguration.java @@ -78,4 +78,11 @@ public interface IPluginConfiguration { * @return */ String getDockerMachine(); + + /** + * The version of the plugin + * + * @return + */ + String getPluginVersion(); } diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/impl/PluginConfiguration.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/impl/PluginConfiguration.java index 1960bb92..82542647 100644 --- a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/impl/PluginConfiguration.java +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/impl/PluginConfiguration.java @@ -22,6 +22,10 @@ import java.util.Map; import java.util.Properties; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; + import com.genericworkflownodes.knime.custom.config.BinaryManager; import com.genericworkflownodes.knime.custom.config.IPluginConfiguration; @@ -68,6 +72,11 @@ public class PluginConfiguration implements IPluginConfiguration { */ private final String m_dockerMachine; + /** + * The version + */ + private final String m_version; + /** * C'tor for {@link PluginConfiguration}. * @@ -87,22 +96,24 @@ public PluginConfiguration(final String pluginId, final String pluginName, m_pluginId = pluginId; m_pluginName = pluginName; m_props = props; + Bundle bundle = FrameworkUtil.getBundle(classFromPlugin); + m_version = bundle.getVersion().toString(); m_binaryManager = new BinaryManager(classFromPlugin); Properties p = new Properties(); Map toolMap = new Hashtable(); - for(String key: m_props.stringPropertyNames()){ - if(key.startsWith("tool.")){ + for (String key: m_props.stringPropertyNames()) { + if (key.startsWith("tool.")) { String value = m_props.getProperty(key); p.put(key, value); String[] keyElements = key.split("\\."); - if(keyElements.length > 2){ + if (keyElements.length > 2) { String tool_key = ""; - for(int i=2;i mt) { @@ -238,5 +243,7 @@ private String mimetypes2String(List mt) { mimetypes.append("]"); return mimetypes.toString(); } + + protected abstract IPluginConfiguration getPluginConfig(); } diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeSetFactory.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeSetFactory.java index 2e97fa74..736c2733 100644 --- a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeSetFactory.java +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeSetFactory.java @@ -19,6 +19,7 @@ import org.knime.core.node.config.ConfigRO; import com.genericworkflownodes.knime.config.reader.CTDConfigurationReader; +import com.genericworkflownodes.knime.custom.config.IPluginConfiguration; public abstract class DynamicGenericNodeSetFactory implements NodeSetFactory { @@ -30,38 +31,36 @@ public abstract class DynamicGenericNodeSetFactory implements NodeSetFactory { * that loads nodes from CTD files in the given source folder. * @param folder the source folder to search for CTDs in. */ - public DynamicGenericNodeSetFactory(String folder) { - m_folder = folder; - m_folderFile = resolveSourceFile(getClass(), folder); + public DynamicGenericNodeSetFactory() { } - private File m_folderFile; - private String m_folder; private Map m_idToFile; + private boolean m_isDeprecated; /** * @return The class of the node factory to use. */ protected abstract Class getNodeFactory(); + /** + * Implement this method and return the configuration of the plugin + * the nodes are hosted in. + * @return the plugin configuration. + */ + protected abstract IPluginConfiguration getPluginConfig(); + protected abstract String getCategoryPath(); - - static File resolveSourceFile(Class clazz, String relPath) { - return Paths.get(clazz - .getProtectionDomain() - .getCodeSource() - .getLocation() - .getPath(), relPath).toFile(); - } protected abstract String getIdForTool(String relPath); + + @Override public Collection getNodeFactoryIds() { if (m_idToFile == null) { m_idToFile = new LinkedHashMap<>(); - for (File f : FileUtils.listFiles(m_folderFile, CTD_EXT, true)) { +/* for (File f : FileUtils.listFiles(m_folderFile, CTD_EXT, true)) { if (f.isFile()) { String name = f.getName(); if (name.endsWith(".xml") && !name.equals("config.xml")) { @@ -72,6 +71,9 @@ public Collection getNodeFactoryIds() { String p = parent.relativize(filePath).toString(); m_idToFile.put(getIdForTool(p), p); } + }*/ + for (String s : getPluginConfig().getBinaryManager().listTools()) { + m_idToFile.put(getIdForTool(s), s); } } return m_idToFile.keySet(); @@ -85,14 +87,14 @@ public Class> getNodeFactory(String i @Override public String getCategoryPath(String id) { String category; - File f = Paths.get(m_folderFile.getAbsolutePath()).resolve(m_idToFile.get(id)).toAbsolutePath().toFile(); + File f = getPluginConfig().getBinaryManager().resolveToolDescriptorPath(m_idToFile.get(id)); try (InputStream cfgStream = new FileInputStream(f)) { category = new CTDConfigurationReader().read(cfgStream).getCategory(); } catch(Exception e) { logger.error("Could not read node category from CTD, using '/' instead.", e); category = ""; } - return getCategoryPath() + "/" + category; + return getCategoryPath() + "/" + category + "/" + getPluginConfig().getPluginVersion(); } @Override @@ -105,8 +107,14 @@ public ConfigRO getAdditionalSettings(String id) { NodeSettings ns = new NodeSettings(""); ns.addString(DynamicGenericNodeFactory.ID_CFG_KEY, id); ns.addString(DynamicGenericNodeFactory.CTD_FILE_CFG_KEY, - Paths.get(m_folder).resolve(m_idToFile.get(id)).toString()); + m_idToFile.get(id)); + ns.addBoolean(DynamicGenericNodeFactory.DEPRECATION_CFG_KEY, + m_isDeprecated); return ns; } + + public void setIsDeprecated(boolean isDeprecated) { + m_isDeprecated = isDeprecated; + } } diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactory.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactory.java new file mode 100644 index 00000000..28c46f5b --- /dev/null +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactory.java @@ -0,0 +1,70 @@ +package com.genericworkflownodes.knime.dynamic; + +import java.util.ArrayList; +import java.util.Collection; + +import org.knime.core.node.NodeFactory; +import org.knime.core.node.NodeModel; +import org.knime.core.node.NodeSetFactory; +import org.knime.core.node.config.ConfigRO; +import org.osgi.framework.Version; + +public class VersionedNodeSetFactory implements NodeSetFactory { + + private final DynamicGenericNodeSetFactory m_nodeSetFactory; + private final String m_versionSuffix; + private final Version m_version; + + public Version getVersion() { + return m_version; + } + + public NodeSetFactory getNodeSetFactory() { + return m_nodeSetFactory; + } + + public VersionedNodeSetFactory(DynamicGenericNodeSetFactory factory) { + m_version = new Version(factory.getPluginConfig().getPluginVersion()); + m_nodeSetFactory = factory; + m_versionSuffix = "_" + factory.getPluginConfig().getPluginVersion().replaceAll("\\.", "_"); + } + + @Override + public Collection getNodeFactoryIds() { + ArrayList ids = new ArrayList<>(); + for (String id : m_nodeSetFactory.getNodeFactoryIds()) { + ids.add(id + m_versionSuffix); + } + return ids; + } + + private String removeSuffix(String id) { + return id.substring(0, id.length() - m_versionSuffix.length()); + } + + @Override + public Class> getNodeFactory( + String id) { + return m_nodeSetFactory.getNodeFactory(removeSuffix(id)); + } + + @Override + public String getCategoryPath(String id) { + return m_nodeSetFactory.getCategoryPath(removeSuffix(id)); + } + + @Override + public String getAfterID(String id) { + return m_nodeSetFactory.getAfterID(removeSuffix(id)); + } + + @Override + public ConfigRO getAdditionalSettings(String id) { + return m_nodeSetFactory.getAdditionalSettings(removeSuffix(id)); + } + + public void setIsDeprecated(boolean isDeprecated) { + m_nodeSetFactory.setIsDeprecated(isDeprecated); + } + +} diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactoryManager.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactoryManager.java new file mode 100644 index 00000000..573df7be --- /dev/null +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactoryManager.java @@ -0,0 +1,134 @@ +package com.genericworkflownodes.knime.dynamic; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.PriorityQueue; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.knime.core.node.NodeFactory; +import org.knime.core.node.NodeLogger; +import org.knime.core.node.NodeModel; +import org.knime.core.node.NodeSetFactory; +import org.knime.core.node.config.ConfigRO; +import org.osgi.framework.Version; + +public class VersionedNodeSetFactoryManager implements NodeSetFactory { + + List m_factories = null; + Map m_idToFac = new HashMap<>(); + + /** + * The id of the used extension point. + */ + private static final String EXTENSION_POINT_ID = "com.genericworkflownodes.knime.dynamic.VersionedNodeSetFactory"; + + /** + * The central static logger. + */ + private static final NodeLogger LOGGER = NodeLogger + .getLogger(VersionedNodeSetFactoryManager.class); + + /* + * Searchs through the eclipse extension point registry for registered + * {@link VersionedNodeSetFactory}s. + */ + public List getAvailableVersionedNodeSetFactories() { + if (m_factories == null) { + m_factories = new ArrayList(); + Map> pluginIDQueues = new HashMap<>(); + IExtensionRegistry reg = Platform.getExtensionRegistry(); + IConfigurationElement[] elements = reg + .getConfigurationElementsFor(EXTENSION_POINT_ID); + try { + for (IConfigurationElement elem : elements) { + final DynamicGenericNodeSetFactory o = (DynamicGenericNodeSetFactory)elem.createExecutableExtension("class"); + // cast is guaranteed to work based on the extension point + // definition + VersionedNodeSetFactory versionedFac = new VersionedNodeSetFactory(o); + m_factories.add(versionedFac); + String pluginID = o.getPluginConfig().getPluginId(); + if (!pluginIDQueues.containsKey(pluginID)) { + pluginIDQueues.put(pluginID, new PriorityQueue(10,new Comparator(){ + + @Override + public int compare(VersionedNodeSetFactory o1, + VersionedNodeSetFactory o2) { + return o1.getVersion().compareTo(o2.getVersion()); + } + + })); + } + pluginIDQueues.get(pluginID).add(versionedFac); + for (String nodeID : versionedFac.getNodeFactoryIds()) { + if (m_idToFac.containsKey(nodeID)) { + LOGGER.warn("Node with ID " + nodeID + " already registered"); + } + m_idToFac.put(nodeID, versionedFac); + } + } + + for (PriorityQueue q : pluginIDQueues.values()) { + q.poll(); //Newest version stays undeprecated + for (VersionedNodeSetFactory f : q) { + f.setIsDeprecated(true); + } + } + } catch (CoreException e) { + LOGGER.warn(e.getMessage()); + } + } + return m_factories; + } + + @Override + public Collection getNodeFactoryIds() { + ArrayList totalFactories = new ArrayList<>(); + for (VersionedNodeSetFactory fac : getAvailableVersionedNodeSetFactories()) { + totalFactories.addAll(fac.getNodeFactoryIds()); + } + return totalFactories; + } + + @Override + public Class> getNodeFactory( + String id) { + VersionedNodeSetFactory fac = getFactory(id); + return fac.getNodeFactory(id); + } + + @Override + public String getCategoryPath(String id) { + VersionedNodeSetFactory fac = getFactory(id); + return fac.getCategoryPath(id); + } + + @Override + public String getAfterID(String id) { + VersionedNodeSetFactory fac = getFactory(id); + return fac.getAfterID(id); + } + + @Override + public ConfigRO getAdditionalSettings(String id) { + VersionedNodeSetFactory fac = getFactory(id); + return fac.getAdditionalSettings(id); + } + + private VersionedNodeSetFactory getFactory(String id) { + VersionedNodeSetFactory fac = m_idToFac.get(id); + if (fac == null) { + throw new IllegalArgumentException("Node with ID " + id + " is not registered."); + } + return fac; + } + +} From eac98953f875734f4f52b20e2dfad508ef0bf78c Mon Sep 17 00:00:00 2001 From: Alexander Fillbrunn Date: Wed, 15 Nov 2017 14:08:46 +0100 Subject: [PATCH 2/2] Changed deprecation mechanism The factory checks with the VersionedNodeSetFactoryManager whether it is deprecated or not. This avoids writing the info into the additional settings, which is wrong because that info is persisted. I also removed the VersionedNodeSetFactory because that stuff can be handled by the DynamicGenericNodeSetFactory, which now by default gets its version from the plugin. And finally I fixed the handling of paths with spaces. --- ...knime.dynamic.VersionedNodeSetFactory.exsd | 9 +-- .../knime/custom/config/BinaryManager.java | 7 +- .../dynamic/DynamicGenericNodeFactory.java | 29 ++++--- .../dynamic/DynamicGenericNodeModel.java | 2 +- .../dynamic/DynamicGenericNodeSetFactory.java | 48 +++++------- .../knime/dynamic/GenericNodeFactory.java | 17 ++++ .../knime/dynamic/GenericNodeSetFactory.java | 27 +++++++ .../dynamic/VersionedNodeSetFactory.java | 5 -- .../VersionedNodeSetFactoryManager.java | 77 +++++++++++-------- 9 files changed, 126 insertions(+), 95 deletions(-) create mode 100644 com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/GenericNodeFactory.java create mode 100644 com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/GenericNodeSetFactory.java diff --git a/com.genericworkflownodes.knime/schema/com.genericworkflownodes.knime.dynamic.VersionedNodeSetFactory.exsd b/com.genericworkflownodes.knime/schema/com.genericworkflownodes.knime.dynamic.VersionedNodeSetFactory.exsd index 3f7013d2..63388174 100644 --- a/com.genericworkflownodes.knime/schema/com.genericworkflownodes.knime.dynamic.VersionedNodeSetFactory.exsd +++ b/com.genericworkflownodes.knime/schema/com.genericworkflownodes.knime.dynamic.VersionedNodeSetFactory.exsd @@ -55,17 +55,10 @@ - + - - - - Semantic versioning of the registered NodeSetFactory - - - diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/BinaryManager.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/BinaryManager.java index 902adb0f..c135cdf6 100644 --- a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/BinaryManager.java +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/custom/config/BinaryManager.java @@ -20,6 +20,7 @@ import java.io.File; import java.io.IOException; +import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Path; @@ -221,16 +222,14 @@ public Iterable listTools() { ArrayList files = new ArrayList<>(); Path p; try { - p = Paths.get(FileLocator.toFileURL(bundle.getResource(DESCRIPTORS_PATH)).toURI()); + p = Paths.get(FileLocator.toFileURL(bundle.getResource(DESCRIPTORS_PATH)).toString()); } catch (Exception ex) { return Collections.emptyList(); } while (e.hasMoreElements()){ try { - Path el = Paths.get(FileLocator.toFileURL(e.nextElement()).toURI()); + Path el = Paths.get(FileLocator.toFileURL(e.nextElement()).toString()); files.add(p.relativize(el).toString()); - } catch (URISyntaxException e1) { - throw new AssertionError("Should not happen", e1); } catch (IOException e1) { e1.printStackTrace(); } diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeFactory.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeFactory.java index c10165aa..a79432b6 100644 --- a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeFactory.java +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeFactory.java @@ -41,8 +41,7 @@ * * @author Fillbrunn, Alexander */ -public abstract class DynamicGenericNodeFactory - extends DynamicNodeFactory { +public abstract class DynamicGenericNodeFactory extends GenericNodeFactory { /** * The configuration key for the ctd file. @@ -50,15 +49,18 @@ public abstract class DynamicGenericNodeFactory public static final String CTD_FILE_CFG_KEY = "ctdFile"; public static final String ID_CFG_KEY = "id"; + + public static final String NSFID_CFG_KEY = "nsfid"; public static final String DEPRECATION_CFG_KEY = "deprecated"; private static final NodeLogger logger = NodeLogger.getLogger(DynamicGenericNodeFactory.class); + private String m_nsfid; private String m_id; private String m_ctdFile; private INodeConfiguration m_config; - private boolean m_isDeprecated = false; + private boolean m_deprecated; protected String getIconPath() { return ""; @@ -68,11 +70,6 @@ public String getId() { return m_id; } - @Override - public boolean isDeprecated() { - return m_isDeprecated; - } - /** * {@inheritDoc} */ @@ -130,7 +127,6 @@ public NodeView createNodeView(final int viewIndex, public boolean hasDialog() { return true; } - /** * {@inheritDoc} */ @@ -149,8 +145,13 @@ public void loadAdditionalFactorySettings(ConfigRO config) throws InvalidSettingsException { m_ctdFile = config.getString(CTD_FILE_CFG_KEY); m_id = config.getString(ID_CFG_KEY); - if (config.containsKey(DEPRECATION_CFG_KEY)) { - m_isDeprecated = config.getBoolean(DEPRECATION_CFG_KEY); + m_nsfid = config.getString(NSFID_CFG_KEY); + try { + m_deprecated = VersionedNodeSetFactoryManager.isFactoryDeprecated(m_nsfid); + } catch (InterruptedException e) { + // This means that loading the nodes was interrupted. + // We simply leave the node non-deprecated. + logger.error(e); } super.loadAdditionalFactorySettings(config); } @@ -159,7 +160,7 @@ public void loadAdditionalFactorySettings(ConfigRO config) public void saveAdditionalFactorySettings(ConfigWO config) { config.addString(CTD_FILE_CFG_KEY, m_ctdFile); config.addString(ID_CFG_KEY, m_id); - config.addBoolean(DEPRECATION_CFG_KEY, m_isDeprecated); + config.addString(NSFID_CFG_KEY, m_nsfid); super.saveAdditionalFactorySettings(config); } @@ -172,6 +173,7 @@ protected NodeDescription createNodeDescription() { // Node KnimeNode node = doc.addNewKnimeNode(); + node.setDeprecated(m_deprecated); node.setName(cfg.getName()); node.setIcon(getIconPath()); node.setType(KnimeNode.Type.MANIPULATOR); @@ -243,7 +245,4 @@ private String mimetypes2String(List mt) { mimetypes.append("]"); return mimetypes.toString(); } - - protected abstract IPluginConfiguration getPluginConfig(); - } diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeModel.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeModel.java index c49e5790..313f321c 100644 --- a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeModel.java +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeModel.java @@ -415,7 +415,7 @@ protected PortObjectSpec[] configure(PortObjectSpec[] inSpecs) URIPortObjectSpec spec = (URIPortObjectSpec) inSpecs[i]; // get MIMEType from incoming port - // TODO: we should check all file extensions, if its more then one + // TODO: we should check all file extensions, if its more than one String mt = MIMEMap.getMIMEType(spec.getFileExtensions().get(0)); // check whether input MIMEType is in list of allowed MIMETypes diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeSetFactory.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeSetFactory.java index 736c2733..da10e983 100644 --- a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeSetFactory.java +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/DynamicGenericNodeSetFactory.java @@ -2,29 +2,26 @@ import java.io.File; import java.io.FileInputStream; -import java.io.FilenameFilter; import java.io.InputStream; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; -import org.apache.commons.io.FileUtils; import org.knime.core.node.NodeFactory; import org.knime.core.node.NodeLogger; import org.knime.core.node.NodeModel; import org.knime.core.node.NodeSetFactory; import org.knime.core.node.NodeSettings; import org.knime.core.node.config.ConfigRO; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.Version; import com.genericworkflownodes.knime.config.reader.CTDConfigurationReader; import com.genericworkflownodes.knime.custom.config.IPluginConfiguration; -public abstract class DynamicGenericNodeSetFactory implements NodeSetFactory { +public abstract class DynamicGenericNodeSetFactory implements GenericNodeSetFactory { private static final NodeLogger logger = NodeLogger.getLogger(DynamicGenericNodeSetFactory.class); - private static String[] CTD_EXT = new String[]{"ctd", "xml"}; /** * Creates a new DynamicGenericNodeSetFactory @@ -32,48 +29,35 @@ public abstract class DynamicGenericNodeSetFactory implements NodeSetFactory { * @param folder the source folder to search for CTDs in. */ public DynamicGenericNodeSetFactory() { + Version v = getVersion(); + m_versionSuffix = String.format("_%d_%d_%d", v.getMajor(), v.getMinor(), v.getMicro()); } + private String m_versionSuffix; private Map m_idToFile; - private boolean m_isDeprecated; /** * @return The class of the node factory to use. */ - protected abstract Class getNodeFactory(); + protected abstract Class getNodeFactory(); /** * Implement this method and return the configuration of the plugin * the nodes are hosted in. * @return the plugin configuration. */ - protected abstract IPluginConfiguration getPluginConfig(); + public abstract IPluginConfiguration getPluginConfig(); protected abstract String getCategoryPath(); protected abstract String getIdForTool(String relPath); - - @Override public Collection getNodeFactoryIds() { if (m_idToFile == null) { m_idToFile = new LinkedHashMap<>(); - -/* for (File f : FileUtils.listFiles(m_folderFile, CTD_EXT, true)) { - if (f.isFile()) { - String name = f.getName(); - if (name.endsWith(".xml") && !name.equals("config.xml")) { - continue; - } - Path parent = Paths.get(m_folderFile.getAbsolutePath()); - Path filePath = Paths.get(f.getAbsolutePath()); - String p = parent.relativize(filePath).toString(); - m_idToFile.put(getIdForTool(p), p); - } - }*/ for (String s : getPluginConfig().getBinaryManager().listTools()) { - m_idToFile.put(getIdForTool(s), s); + m_idToFile.put(getIdForTool(s) + m_versionSuffix, s); } } return m_idToFile.keySet(); @@ -108,13 +92,17 @@ public ConfigRO getAdditionalSettings(String id) { ns.addString(DynamicGenericNodeFactory.ID_CFG_KEY, id); ns.addString(DynamicGenericNodeFactory.CTD_FILE_CFG_KEY, m_idToFile.get(id)); - ns.addBoolean(DynamicGenericNodeFactory.DEPRECATION_CFG_KEY, - m_isDeprecated); + ns.addString(DynamicGenericNodeFactory.NSFID_CFG_KEY, getId()); return ns; } - public void setIsDeprecated(boolean isDeprecated) { - m_isDeprecated = isDeprecated; + @Override + public String getId() { + return getClass().getCanonicalName() + m_versionSuffix; + } + + @Override + public Version getVersion() { + return FrameworkUtil.getBundle(getClass()).getVersion(); } - } diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/GenericNodeFactory.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/GenericNodeFactory.java new file mode 100644 index 00000000..32c17e2f --- /dev/null +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/GenericNodeFactory.java @@ -0,0 +1,17 @@ +package com.genericworkflownodes.knime.dynamic; + +import org.knime.core.node.DynamicNodeFactory; + +import com.genericworkflownodes.knime.custom.config.IPluginConfiguration; + +public abstract class GenericNodeFactory + extends DynamicNodeFactory { + + protected String getIconPath() { + return ""; + } + + public abstract String getId(); + + protected abstract IPluginConfiguration getPluginConfig(); +} diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/GenericNodeSetFactory.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/GenericNodeSetFactory.java new file mode 100644 index 00000000..b25d6352 --- /dev/null +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/GenericNodeSetFactory.java @@ -0,0 +1,27 @@ +package com.genericworkflownodes.knime.dynamic; + +import org.knime.core.node.NodeSetFactory; +import org.osgi.framework.Version; + +import com.genericworkflownodes.knime.custom.config.IPluginConfiguration; + +public interface GenericNodeSetFactory extends NodeSetFactory { + /** + * The version of the node's created by this factory. + * @return the version + */ + Version getVersion(); + + /** + * The ID identifying the group of nodes provided by this factory. + * @return an ID that is the same for all factories providing + * versions of the same node set. + */ + String getId(); + + /** + * The plugin configuration of this node set factory. + * @return the configuration + */ + IPluginConfiguration getPluginConfig(); +} diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactory.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactory.java index 28c46f5b..e7635a90 100644 --- a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactory.java +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactory.java @@ -62,9 +62,4 @@ public String getAfterID(String id) { public ConfigRO getAdditionalSettings(String id) { return m_nodeSetFactory.getAdditionalSettings(removeSuffix(id)); } - - public void setIsDeprecated(boolean isDeprecated) { - m_nodeSetFactory.setIsDeprecated(isDeprecated); - } - } diff --git a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactoryManager.java b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactoryManager.java index 573df7be..f92d50e3 100644 --- a/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactoryManager.java +++ b/com.genericworkflownodes.knime/src/com/genericworkflownodes/knime/dynamic/VersionedNodeSetFactoryManager.java @@ -4,9 +4,9 @@ import java.util.Collection; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.PriorityQueue; import java.util.Set; @@ -19,17 +19,17 @@ import org.knime.core.node.NodeModel; import org.knime.core.node.NodeSetFactory; import org.knime.core.node.config.ConfigRO; -import org.osgi.framework.Version; public class VersionedNodeSetFactoryManager implements NodeSetFactory { - List m_factories = null; - Map m_idToFac = new HashMap<>(); + List m_factories = null; + Map m_idToFac = new HashMap<>(); /** * The id of the used extension point. */ - private static final String EXTENSION_POINT_ID = "com.genericworkflownodes.knime.dynamic.VersionedNodeSetFactory"; + private static final String EXTENSION_POINT_ID = + "com.genericworkflownodes.knime.dynamic.VersionedNodeSetFactory"; /** * The central static logger. @@ -41,46 +41,46 @@ public class VersionedNodeSetFactoryManager implements NodeSetFactory { * Searchs through the eclipse extension point registry for registered * {@link VersionedNodeSetFactory}s. */ - public List getAvailableVersionedNodeSetFactories() { + public synchronized List getAvailableVersionedNodeSetFactories() { if (m_factories == null) { - m_factories = new ArrayList(); - Map> pluginIDQueues = new HashMap<>(); + m_factories = new ArrayList(); + Map> pluginIDQueues = new HashMap<>(); IExtensionRegistry reg = Platform.getExtensionRegistry(); IConfigurationElement[] elements = reg .getConfigurationElementsFor(EXTENSION_POINT_ID); try { for (IConfigurationElement elem : elements) { - final DynamicGenericNodeSetFactory o = (DynamicGenericNodeSetFactory)elem.createExecutableExtension("class"); + final GenericNodeSetFactory o = (GenericNodeSetFactory)elem.createExecutableExtension("class"); // cast is guaranteed to work based on the extension point // definition - VersionedNodeSetFactory versionedFac = new VersionedNodeSetFactory(o); - m_factories.add(versionedFac); + m_factories.add(o); String pluginID = o.getPluginConfig().getPluginId(); if (!pluginIDQueues.containsKey(pluginID)) { - pluginIDQueues.put(pluginID, new PriorityQueue(10,new Comparator(){ - + pluginIDQueues.put(pluginID, new PriorityQueue(10, new Comparator(){ @Override - public int compare(VersionedNodeSetFactory o1, - VersionedNodeSetFactory o2) { - return o1.getVersion().compareTo(o2.getVersion()); + public int compare(GenericNodeSetFactory o1, + GenericNodeSetFactory o2) { + return o2.getVersion().compareTo(o1.getVersion()); } - })); } - pluginIDQueues.get(pluginID).add(versionedFac); - for (String nodeID : versionedFac.getNodeFactoryIds()) { + pluginIDQueues.get(pluginID).add(o); + for (String nodeID : o.getNodeFactoryIds()) { if (m_idToFac.containsKey(nodeID)) { LOGGER.warn("Node with ID " + nodeID + " already registered"); } - m_idToFac.put(nodeID, versionedFac); + m_idToFac.put(nodeID, o); } } - for (PriorityQueue q : pluginIDQueues.values()) { - q.poll(); //Newest version stays undeprecated - for (VersionedNodeSetFactory f : q) { - f.setIsDeprecated(true); - } + for (PriorityQueue q : pluginIDQueues.values()) { + //Newest version stays undeprecated + GenericNodeSetFactory latest = q.poll(); + m_nondeprecatedFactories.add(latest.getId()); + } + synchronized (m_nondeprecatedFactories) { + m_loaded = true; + m_nondeprecatedFactories.notifyAll(); } } catch (CoreException e) { LOGGER.warn(e.getMessage()); @@ -89,10 +89,23 @@ public int compare(VersionedNodeSetFactory o1, return m_factories; } + private static boolean m_loaded = false; + private static Set m_nondeprecatedFactories = new HashSet<>(); + + public static boolean isFactoryDeprecated(String id) throws InterruptedException { + // We need to wait here until all nodes are loaded to determine which ones are deprecated + synchronized (m_nondeprecatedFactories) { + while (!m_loaded) { + m_nondeprecatedFactories.wait(); + } + } + return !m_nondeprecatedFactories.contains(id); + } + @Override public Collection getNodeFactoryIds() { ArrayList totalFactories = new ArrayList<>(); - for (VersionedNodeSetFactory fac : getAvailableVersionedNodeSetFactories()) { + for (GenericNodeSetFactory fac : getAvailableVersionedNodeSetFactories()) { totalFactories.addAll(fac.getNodeFactoryIds()); } return totalFactories; @@ -101,30 +114,30 @@ public Collection getNodeFactoryIds() { @Override public Class> getNodeFactory( String id) { - VersionedNodeSetFactory fac = getFactory(id); + GenericNodeSetFactory fac = getFactory(id); return fac.getNodeFactory(id); } @Override public String getCategoryPath(String id) { - VersionedNodeSetFactory fac = getFactory(id); + GenericNodeSetFactory fac = getFactory(id); return fac.getCategoryPath(id); } @Override public String getAfterID(String id) { - VersionedNodeSetFactory fac = getFactory(id); + GenericNodeSetFactory fac = getFactory(id); return fac.getAfterID(id); } @Override public ConfigRO getAdditionalSettings(String id) { - VersionedNodeSetFactory fac = getFactory(id); + GenericNodeSetFactory fac = getFactory(id); return fac.getAdditionalSettings(id); } - private VersionedNodeSetFactory getFactory(String id) { - VersionedNodeSetFactory fac = m_idToFac.get(id); + private GenericNodeSetFactory getFactory(String id) { + GenericNodeSetFactory fac = m_idToFac.get(id); if (fac == null) { throw new IllegalArgumentException("Node with ID " + id + " is not registered."); }