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