Decouple plugins from their "jar" external form
Until now all the server-side plugins have been associated to a single jar file in the /plugin directory. As first step to allow different forms of plugins (e.g. script files, directories or anything else that can provide classes and resources) we need to de-couple the underlying Jar file from the server side plugin. We introduce the concept of "plugin-scanner" as the interface to scan the external form to discover: - plugin classes - plugin resources - plugin meta-data (i.e. Manifest) Change-Id: I769595a030545a5f272f453c3cf435b74719e1e7
This commit is contained in:
		| @@ -29,6 +29,7 @@ import com.google.gerrit.server.documentation.MarkdownFormatter; | ||||
| import com.google.gerrit.server.plugins.Plugin; | ||||
| import com.google.gerrit.server.plugins.PluginsCollection; | ||||
| import com.google.gerrit.server.plugins.ReloadPluginListener; | ||||
| import com.google.gerrit.server.plugins.ServerPlugin; | ||||
| import com.google.gerrit.server.plugins.StartPluginListener; | ||||
| import com.google.gerrit.server.ssh.SshInfo; | ||||
| import com.google.gwtexpui.server.CacheHeaders; | ||||
| @@ -268,7 +269,7 @@ class HttpPluginServlet extends HttpServlet | ||||
|     } | ||||
|  | ||||
|     if (file.startsWith(holder.staticPrefix)) { | ||||
|       JarFile jar = holder.plugin.getJarFile(); | ||||
|       JarFile jar = jarFileOf(holder.plugin); | ||||
|       if (jar != null) { | ||||
|         JarEntry entry = jar.getJarEntry(file); | ||||
|         if (exists(entry)) { | ||||
| @@ -286,7 +287,7 @@ class HttpPluginServlet extends HttpServlet | ||||
|     } else if (file.startsWith(holder.docPrefix) && file.endsWith("/")) { | ||||
|       res.sendRedirect(uri + "index.html"); | ||||
|     } else if (file.startsWith(holder.docPrefix)) { | ||||
|       JarFile jar = holder.plugin.getJarFile(); | ||||
|       JarFile jar = jarFileOf(holder.plugin); | ||||
|       JarEntry entry = jar.getJarEntry(file); | ||||
|       if (!exists(entry)) { | ||||
|         entry = findSource(jar, file); | ||||
| @@ -632,6 +633,14 @@ class HttpPluginServlet extends HttpServlet | ||||
|     return data; | ||||
|   } | ||||
|  | ||||
|   private static JarFile jarFileOf(Plugin plugin) { | ||||
|     if(plugin instanceof ServerPlugin) { | ||||
|       return ((ServerPlugin) plugin).getJarFile(); | ||||
|     } else { | ||||
|       return null; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private static class PluginHolder { | ||||
|     final Plugin plugin; | ||||
|     final GuiceFilter filter; | ||||
| @@ -648,7 +657,7 @@ class HttpPluginServlet extends HttpServlet | ||||
|     } | ||||
|  | ||||
|     private static String getPrefix(Plugin plugin, String attr, String def) { | ||||
|       JarFile jarFile = plugin.getJarFile(); | ||||
|       JarFile jarFile = jarFileOf(plugin); | ||||
|       if (jarFile == null) { | ||||
|         return def; | ||||
|       } | ||||
|   | ||||
| @@ -23,7 +23,7 @@ import com.google.common.collect.Sets; | ||||
| import com.google.gerrit.extensions.annotations.Export; | ||||
| import com.google.gerrit.extensions.annotations.ExtensionPoint; | ||||
| import com.google.gerrit.extensions.annotations.Listen; | ||||
| import com.google.gerrit.server.plugins.JarScanner.ExtensionMetaData; | ||||
| import com.google.gerrit.server.plugins.PluginContentScanner.ExtensionMetaData; | ||||
| import com.google.inject.AbstractModule; | ||||
| import com.google.inject.Module; | ||||
| import com.google.inject.Scopes; | ||||
| @@ -34,12 +34,11 @@ import java.lang.reflect.ParameterizedType; | ||||
| import java.util.Arrays; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
| import java.util.jar.JarFile; | ||||
|  | ||||
| class AutoRegisterModules { | ||||
|   private final String pluginName; | ||||
|   private final PluginGuiceEnvironment env; | ||||
|   private final JarFile jarFile; | ||||
|   private final PluginContentScanner scanner; | ||||
|   private final ClassLoader classLoader; | ||||
|   private final ModuleGenerator sshGen; | ||||
|   private final ModuleGenerator httpGen; | ||||
| @@ -53,11 +52,11 @@ class AutoRegisterModules { | ||||
|  | ||||
|   AutoRegisterModules(String pluginName, | ||||
|       PluginGuiceEnvironment env, | ||||
|       JarFile jarFile, | ||||
|       PluginContentScanner scanner, | ||||
|       ClassLoader classLoader) { | ||||
|     this.pluginName = pluginName; | ||||
|     this.env = env; | ||||
|     this.jarFile = jarFile; | ||||
|     this.scanner = scanner; | ||||
|     this.classLoader = classLoader; | ||||
|     this.sshGen = env.hasSshModule() ? env.newSshModuleGenerator() : null; | ||||
|     this.httpGen = env.hasHttpModule() ? env.newHttpModuleGenerator() : null; | ||||
| @@ -111,7 +110,7 @@ class AutoRegisterModules { | ||||
|  | ||||
|   private void scan() throws InvalidPluginException { | ||||
|     Map<Class<? extends Annotation>, Iterable<ExtensionMetaData>> extensions = | ||||
|         JarScanner.scan(jarFile, pluginName, Arrays.asList(Export.class, Listen.class)); | ||||
|         scanner.scan(pluginName, Arrays.asList(Export.class, Listen.class)); | ||||
|     for (ExtensionMetaData export : extensions.get(Export.class)) { | ||||
|       export(export); | ||||
|     } | ||||
| @@ -123,18 +122,18 @@ class AutoRegisterModules { | ||||
|   private void export(ExtensionMetaData def) throws InvalidPluginException { | ||||
|     Class<?> clazz; | ||||
|     try { | ||||
|       clazz = Class.forName(def.getClassName(), false, classLoader); | ||||
|       clazz = Class.forName(def.className, false, classLoader); | ||||
|     } catch (ClassNotFoundException err) { | ||||
|       throw new InvalidPluginException(String.format( | ||||
|           "Cannot load %s with @Export(\"%s\")", | ||||
|           def.getClassName(), def.getAnnotationValue()), err); | ||||
|           def.className, def.annotationValue), err); | ||||
|     } | ||||
|  | ||||
|     Export export = clazz.getAnnotation(Export.class); | ||||
|     if (export == null) { | ||||
|       PluginLoader.log.warn(String.format( | ||||
|           "In plugin %s asm incorrectly parsed %s with @Export(\"%s\")", | ||||
|           pluginName, clazz.getName(), def.getAnnotationValue())); | ||||
|           pluginName, clazz.getName(), def.annotationValue)); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| @@ -162,11 +161,11 @@ class AutoRegisterModules { | ||||
|   private void listen(ExtensionMetaData def) throws InvalidPluginException { | ||||
|     Class<?> clazz; | ||||
|     try { | ||||
|       clazz = Class.forName(def.getClassName(), false, classLoader); | ||||
|       clazz = Class.forName(def.className, false, classLoader); | ||||
|     } catch (ClassNotFoundException err) { | ||||
|       throw new InvalidPluginException(String.format( | ||||
|           "Cannot load %s with @Listen", | ||||
|           def.getClassName()), err); | ||||
|           def.className), err); | ||||
|     } | ||||
|  | ||||
|     Listen listen = clazz.getAnnotation(Listen.class); | ||||
|   | ||||
| @@ -24,6 +24,7 @@ import com.google.common.base.Strings; | ||||
| import com.google.common.collect.ArrayListMultimap; | ||||
| import com.google.common.collect.ImmutableMap; | ||||
| import com.google.common.collect.Iterables; | ||||
| import com.google.common.collect.Lists; | ||||
| import com.google.common.collect.Maps; | ||||
| import com.google.common.collect.Multimap; | ||||
| import com.google.common.collect.Sets; | ||||
| @@ -38,6 +39,7 @@ import org.objectweb.asm.MethodVisitor; | ||||
| import org.objectweb.asm.Opcodes; | ||||
| import org.objectweb.asm.Type; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.lang.annotation.Annotation; | ||||
| @@ -46,10 +48,12 @@ import java.util.Collections; | ||||
| import java.util.Enumeration; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
| import java.util.jar.Attributes; | ||||
| import java.util.jar.JarEntry; | ||||
| import java.util.jar.JarFile; | ||||
| import java.util.jar.Manifest; | ||||
|  | ||||
| public class JarScanner { | ||||
| public class JarScanner implements PluginContentScanner { | ||||
|   private static final int SKIP_ALL = ClassReader.SKIP_CODE | ||||
|       | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; | ||||
|   private static final Function<ClassData, ExtensionMetaData> CLASS_DATA_TO_EXTENSION_META_DATA = | ||||
| @@ -61,27 +65,19 @@ public class JarScanner { | ||||
|         } | ||||
|       }; | ||||
|  | ||||
|   public static class ExtensionMetaData { | ||||
|     private final String className; | ||||
|     private final String annotationValue; | ||||
|   private final JarFile jarFile; | ||||
|  | ||||
|     private ExtensionMetaData(String className, String annotationValue) { | ||||
|       this.className = className; | ||||
|       this.annotationValue = annotationValue; | ||||
|     } | ||||
|  | ||||
|     public String getAnnotationValue() { | ||||
|       return annotationValue; | ||||
|     } | ||||
|  | ||||
|     public String getClassName() { | ||||
|       return className; | ||||
|   public JarScanner(File srcFile) throws InvalidPluginException { | ||||
|     try { | ||||
|       this.jarFile = new JarFile(srcFile); | ||||
|     } catch (IOException e) { | ||||
|       throw new InvalidPluginException("Cannot scan plugin file " + srcFile, e); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public static Map<Class<? extends Annotation>, Iterable<ExtensionMetaData>> scan( | ||||
|       JarFile jarFile, String pluginName, | ||||
|       Iterable<Class<? extends Annotation>> annotations) | ||||
|   @Override | ||||
|   public Map<Class<? extends Annotation>, Iterable<ExtensionMetaData>> scan( | ||||
|       String pluginName, Iterable<Class<? extends Annotation>> annotations) | ||||
|       throws InvalidPluginException { | ||||
|     Set<String> descriptors = Sets.newHashSet(); | ||||
|     Multimap<String, JarScanner.ClassData> rawMap = ArrayListMultimap.create(); | ||||
| @@ -262,4 +258,62 @@ public class JarScanner { | ||||
|     public void visitEnd() { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public Optional<PluginEntry> getEntry(String resourcePath) throws IOException { | ||||
|     JarEntry jarEntry = jarFile.getJarEntry(resourcePath); | ||||
|     if (jarEntry == null || jarEntry.getSize() == 0) { | ||||
|       return Optional.absent(); | ||||
|     } | ||||
|  | ||||
|     return Optional.of(resourceOf(jarEntry)); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public Enumeration<PluginEntry> entries() { | ||||
|     return Collections.enumeration(Lists.transform( | ||||
|         Collections.list(jarFile.entries()), | ||||
|         new Function<JarEntry, PluginEntry>() { | ||||
|           public PluginEntry apply(JarEntry jarEntry) { | ||||
|             try { | ||||
|               return resourceOf(jarEntry); | ||||
|             } catch (IOException e) { | ||||
|               throw new IllegalArgumentException("Cannot convert jar entry " | ||||
|                   + jarEntry + " to a resource", e); | ||||
|             } | ||||
|           } | ||||
|         })); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public InputStream getInputStream(PluginEntry entry) | ||||
|       throws IOException { | ||||
|     return jarFile.getInputStream(jarFile | ||||
|         .getEntry(entry.getName())); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public Manifest getManifest() throws IOException { | ||||
|     return jarFile.getManifest(); | ||||
|   } | ||||
|  | ||||
|   private PluginEntry resourceOf(JarEntry jarEntry) throws IOException { | ||||
|     return new PluginEntry(jarEntry.getName(), jarEntry.getTime(), | ||||
|         jarEntry.getSize(), attributesOf(jarEntry)); | ||||
|   } | ||||
|  | ||||
|   private Map<Object, String> attributesOf(JarEntry jarEntry) | ||||
|       throws IOException { | ||||
|     Attributes attributes = jarEntry.getAttributes(); | ||||
|     if (attributes == null) { | ||||
|       return Collections.emptyMap(); | ||||
|     } | ||||
|     return Maps.transformEntries(attributes, | ||||
|         new Maps.EntryTransformer<Object, Object, String>() { | ||||
|           @Override | ||||
|           public String transformEntry(Object key, Object value) { | ||||
|             return (String) value; | ||||
|           } | ||||
|         }); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -66,11 +66,6 @@ class JsPlugin extends Plugin { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public JarFile getJarFile() { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public Injector getSysInjector() { | ||||
|     return null; | ||||
| @@ -109,4 +104,9 @@ class JsPlugin extends Plugin { | ||||
|           new JavaScriptPlugin(fileName)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public PluginContentScanner getContentScanner() { | ||||
|     return PluginContentScanner.EMPTY; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -137,7 +137,7 @@ public class ListPlugins implements RestReadView<TopLevelResource> { | ||||
|       version = p.getVersion(); | ||||
|       disabled = p.isDisabled() ? true : null; | ||||
|  | ||||
|       if (p.getJarFile() != null) { | ||||
|       if (p.getSrcFile() != null) { | ||||
|         indexUrl = String.format("plugins/%s/", p.getName()); | ||||
|       } | ||||
|     } | ||||
|   | ||||
| @@ -124,7 +124,7 @@ public abstract class Plugin { | ||||
|  | ||||
|   abstract void stop(PluginGuiceEnvironment env); | ||||
|  | ||||
|   public abstract JarFile getJarFile(); | ||||
|   public abstract PluginContentScanner getContentScanner(); | ||||
|  | ||||
|   public abstract Injector getSysInjector(); | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,130 @@ | ||||
| // Copyright (C) 2014 The Android Open Source Project | ||||
| // | ||||
| // 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 com.google.gerrit.server.plugins; | ||||
|  | ||||
| import com.google.common.base.Optional; | ||||
|  | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.lang.annotation.Annotation; | ||||
| import java.util.Collections; | ||||
| import java.util.Enumeration; | ||||
| import java.util.Map; | ||||
| import java.util.jar.Manifest; | ||||
|  | ||||
| /** | ||||
|  * Scans the plugin returning classes and resources. | ||||
|  * | ||||
|  * Gerrit uses the scanner to automatically discover the classes | ||||
|  * and resources exported by the plugin for auto discovery | ||||
|  * of exported SSH commands, Servlets and listeners. | ||||
|  */ | ||||
| public interface PluginContentScanner { | ||||
|  | ||||
|   /** | ||||
|    * Scanner without resources. | ||||
|    */ | ||||
|   PluginContentScanner EMPTY = new PluginContentScanner() { | ||||
|     @Override | ||||
|     public Manifest getManifest() throws IOException { | ||||
|       return new Manifest(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Map<Class<? extends Annotation>, Iterable<ExtensionMetaData>> scan( | ||||
|         String pluginName, Iterable<Class<? extends Annotation>> annotations) | ||||
|         throws InvalidPluginException { | ||||
|      return Collections.emptyMap(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Optional<PluginEntry> getEntry(String resourcePath) | ||||
|         throws IOException { | ||||
|       return Optional.absent(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public InputStream getInputStream(PluginEntry entry) throws IOException { | ||||
|       throw new FileNotFoundException("Empty plugin"); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Enumeration<PluginEntry> entries() { | ||||
|       return Collections.emptyEnumeration(); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * Plugin class extension meta-data | ||||
|    * | ||||
|    * Class name and annotation value of the class | ||||
|    * provided by a plugin to extend an existing | ||||
|    * extension point in Gerrit. | ||||
|    */ | ||||
|   public static class ExtensionMetaData { | ||||
|     public final String className; | ||||
|     public final String annotationValue; | ||||
|  | ||||
|     public ExtensionMetaData(String className, String annotationValue) { | ||||
|       this.className = className; | ||||
|       this.annotationValue = annotationValue; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Return the plugin meta-data manifest | ||||
|    * | ||||
|    * @return Manifest of the plugin or null if plugin has no meta-data | ||||
|    * @throws IOException if an I/O problem occurred whilst accessing the Manifest | ||||
|    */ | ||||
|   Manifest getManifest() throws IOException; | ||||
|  | ||||
|   /** | ||||
|    * Scans the plugin for declared public annotated classes | ||||
|    * | ||||
|    * @param pluginName the plugin name | ||||
|    * @param annotations annotations declared by the plugin classes | ||||
|    * @return map of annotations and associated plugin classes found | ||||
|    * @throws InvalidPluginException if the plugin is not valid or corrupted | ||||
|    */ | ||||
|   Map<Class<? extends Annotation>, Iterable<ExtensionMetaData>> scan( | ||||
|       String pluginName, Iterable<Class<? extends Annotation>> annotations) | ||||
|       throws InvalidPluginException; | ||||
|  | ||||
|   /** | ||||
|    * Return the plugin resource associated to a path | ||||
|    * | ||||
|    * @param resourcePath full path of the resource inside the plugin package | ||||
|    * @return the resource object or Optional.absent() if the resource was not found | ||||
|    * @throws IOException if there was a problem retrieving the resource | ||||
|    */ | ||||
|   Optional<PluginEntry> getEntry(String resourcePath) throws IOException; | ||||
|  | ||||
|   /** | ||||
|    * Return the InputStream of the resource entry | ||||
|    * | ||||
|    * @param entry resource entry inside the plugin package | ||||
|    * @return the resource input stream | ||||
|    * @throws IOException if there was an I/O problem accessing the resource | ||||
|    */ | ||||
|   InputStream getInputStream(PluginEntry entry) throws IOException; | ||||
|  | ||||
|   /** | ||||
|    * Return all the resources inside a plugin | ||||
|    * | ||||
|    * @return the enumeration of all resources found | ||||
|    */ | ||||
|   Enumeration<PluginEntry> entries(); | ||||
| } | ||||
| @@ -0,0 +1,64 @@ | ||||
| // Copyright (C) 2014 The Android Open Source Project | ||||
| // | ||||
| // 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 com.google.gerrit.server.plugins; | ||||
|  | ||||
| import java.util.Collections; | ||||
| import java.util.Map; | ||||
|  | ||||
| /** | ||||
|  * Plugin static resource entry | ||||
|  * | ||||
|  * Bean representing a static resource inside a plugin. | ||||
|  * All static resources are available at <plugin web url>/static | ||||
|  * and served by the HttpPluginServlet. | ||||
|  */ | ||||
| public class PluginEntry { | ||||
|   public static final String ATTR_CHARACTER_ENCODING = "Character-Encoding"; | ||||
|   public static final String ATTR_CONTENT_TYPE = "Content-Type"; | ||||
|  | ||||
|   private static final Map<Object,String> EMPTY_ATTRS = Collections.emptyMap(); | ||||
|  | ||||
|   private final String name; | ||||
|   private final long time; | ||||
|   private final long size; | ||||
|   private final Map<Object, String> attrs; | ||||
|  | ||||
|   public PluginEntry(String name, long time, long size, | ||||
|       Map<Object, String> attrs) { | ||||
|     this.name = name; | ||||
|     this.time = time; | ||||
|     this.size = size; | ||||
|     this.attrs = attrs; | ||||
|   } | ||||
|  | ||||
|   public PluginEntry(String name, long time, long size) { | ||||
|     this(name, time, size, EMPTY_ATTRS); | ||||
|   } | ||||
|  | ||||
|   public String getName() { | ||||
|     return name; | ||||
|   } | ||||
|  | ||||
|   public long getTime() { | ||||
|     return time; | ||||
|   } | ||||
|  | ||||
|   public long getSize() { | ||||
|     return size; | ||||
|   } | ||||
|  | ||||
|   public Map<Object, String> getAttrs() { | ||||
|     return attrs; | ||||
|   } | ||||
| } | ||||
| @@ -589,8 +589,8 @@ public class PluginLoader implements LifecycleListener { | ||||
|  | ||||
|       Plugin plugin = new ServerPlugin(name, url, | ||||
|           pluginUserFactory.create(name), | ||||
|           srcJar, snapshot, | ||||
|           jarFile, manifest, | ||||
|           srcJar, snapshot, new JarFile(srcJar), | ||||
|           new JarScanner(srcJar), | ||||
|           new File(dataDir, name), type, pluginLoader, | ||||
|           sysModule, sshModule, httpModule); | ||||
|       cleanupHandles.put(plugin, new CleanupHandle(tmp, jarFile)); | ||||
|   | ||||
| @@ -35,12 +35,13 @@ import com.google.inject.ProvisionException; | ||||
| import org.eclipse.jgit.internal.storage.file.FileSnapshot; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.util.List; | ||||
| import java.util.jar.Attributes; | ||||
| import java.util.jar.JarFile; | ||||
| import java.util.jar.Manifest; | ||||
|  | ||||
| class ServerPlugin extends Plugin { | ||||
| public class ServerPlugin extends Plugin { | ||||
|  | ||||
|   /** Unique key that changes whenever a plugin reloads. */ | ||||
|   public static final class CacheKey { | ||||
| @@ -59,6 +60,7 @@ class ServerPlugin extends Plugin { | ||||
|  | ||||
|   private final JarFile jarFile; | ||||
|   private final Manifest manifest; | ||||
|   private final PluginContentScanner scanner; | ||||
|   private final File dataDir; | ||||
|   private final String pluginCanonicalWebUrl; | ||||
|   private final ClassLoader classLoader; | ||||
| @@ -78,28 +80,39 @@ class ServerPlugin extends Plugin { | ||||
|       File srcJar, | ||||
|       FileSnapshot snapshot, | ||||
|       JarFile jarFile, | ||||
|       Manifest manifest, | ||||
|       PluginContentScanner scanner, | ||||
|       File dataDir, | ||||
|       ApiType apiType, | ||||
|       ClassLoader classLoader, | ||||
|       @Nullable Class<? extends Module> sysModule, | ||||
|       @Nullable Class<? extends Module> sshModule, | ||||
|       @Nullable Class<? extends Module> httpModule) { | ||||
|       @Nullable Class<? extends Module> httpModule) | ||||
|       throws InvalidPluginException { | ||||
|     super(name, srcJar, pluginUser, snapshot, apiType); | ||||
|     this.pluginCanonicalWebUrl = pluginCanonicalWebUrl; | ||||
|     this.jarFile = jarFile; | ||||
|     this.manifest = manifest; | ||||
|     this.scanner = scanner; | ||||
|     this.dataDir = dataDir; | ||||
|     this.classLoader = classLoader; | ||||
|     this.sysModule = sysModule; | ||||
|     this.sshModule = sshModule; | ||||
|     this.httpModule = httpModule; | ||||
|     this.manifest = getPluginManifest(scanner); | ||||
|   } | ||||
|  | ||||
|   File getSrcJar() { | ||||
|     return getSrcFile(); | ||||
|   } | ||||
|  | ||||
|   private Manifest getPluginManifest(PluginContentScanner scanner) | ||||
|       throws InvalidPluginException { | ||||
|     try { | ||||
|        return scanner.getManifest(); | ||||
|     } catch (IOException e) { | ||||
|       throw new InvalidPluginException("Cannot get plugin manifest", e); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Nullable | ||||
|   public String getVersion() { | ||||
|     Attributes main = manifest.getMainAttributes(); | ||||
| @@ -136,7 +149,7 @@ class ServerPlugin extends Plugin { | ||||
|  | ||||
|     AutoRegisterModules auto = null; | ||||
|     if (sysModule == null && sshModule == null && httpModule == null) { | ||||
|       auto = new AutoRegisterModules(getName(), env, jarFile, classLoader); | ||||
|       auto = new AutoRegisterModules(getName(), env, scanner, classLoader); | ||||
|       auto.discover(); | ||||
|     } | ||||
|  | ||||
| @@ -270,4 +283,9 @@ class ServerPlugin extends Plugin { | ||||
|       manager.add(handle); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public PluginContentScanner getContentScanner() { | ||||
|     return scanner; | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Luca Milanesio
					Luca Milanesio