Serve static resources from directory-based Scala scripts
When using Scala scripts inside a plugin directory, the requests
to static/<some relative path> will return the static resources
under the static subdirectory of the Scala script.
This change allows to delivery a fully feature client + server
plugin experience by leveraging HTTP/JS client UX with server-side
Scala script controllers.
ScalaPluginProvider interface aligned with the latest
changes under review on Gerrit I1d07343
Change-Id: Ib6f026197556348c2b15cc29418da6cbf4b85618
diff --git a/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginProvider.java b/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginProvider.java
index 27b7490..9eeffb5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginProvider.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginProvider.java
@@ -14,7 +14,7 @@
package com.googlesource.gerrit.plugins.scripting.scala;
-import com.google.gerrit.server.PluginUser;
+import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.server.plugins.InvalidPluginException;
import com.google.gerrit.server.plugins.ServerPlugin;
import com.google.gerrit.server.plugins.ServerPluginProvider;
@@ -51,21 +51,25 @@
public static final String SCALA_EXTENSION = ".scala";
private final Provider<ScalaPluginScriptEngine> scriptEngineProvider;
+ private final String providerPluginName;
@Inject
- public ScalaPluginProvider(Provider<ScalaPluginScriptEngine> scriptEngineProvider) {
+ public ScalaPluginProvider(
+ Provider<ScalaPluginScriptEngine> scriptEngineProvider,
+ @PluginName String providerPluginName) {
this.scriptEngineProvider = scriptEngineProvider;
+ this.providerPluginName = providerPluginName;
}
@Override
- public ServerPlugin get(File srcFile, PluginUser pluginUser,
- FileSnapshot snapshot, String pluginCanonicalWebUrl, File pluginDataDir)
+ public ServerPlugin get(File srcFile,
+ FileSnapshot snapshot, PluginDescription description)
throws InvalidPluginException {
ScalaPluginScriptEngine scriptEngine = scriptEngineProvider.get();
String name = getPluginName(srcFile);
- return new ServerPlugin(name, pluginCanonicalWebUrl, pluginUser, srcFile,
+ return new ServerPlugin(name, description.canonicalUrl, description.user, srcFile,
snapshot, new ScalaPluginScanner(name, srcFile, scriptEngine),
- pluginDataDir, scriptEngine.getClassLoader());
+ description.dataDir, scriptEngine.getClassLoader());
}
@Override
@@ -82,5 +86,10 @@
}
return srcFileName.substring(0, endPos);
}
+
+ @Override
+ public String getProviderPluginName() {
+ return providerPluginName;
+ }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginScanner.java b/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginScanner.java
index e72fc72..18efe63 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginScanner.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginScanner.java
@@ -14,33 +14,48 @@
package com.googlesource.gerrit.plugins.scripting.scala;
import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
import com.google.gerrit.server.plugins.AbstractPreloadedPluginScanner;
import com.google.gerrit.server.plugins.InvalidPluginException;
import com.google.gerrit.server.plugins.Plugin;
+import com.google.gerrit.server.plugins.PluginEntry;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.FileVisitOption;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.Enumeration;
+import java.util.List;
import java.util.Set;
public class ScalaPluginScanner extends AbstractPreloadedPluginScanner {
+ private final File staticResourcesPath;
+
public ScalaPluginScanner(String pluginName, File srcFile,
- ScalaPluginScriptEngine scriptEngine)
- throws InvalidPluginException {
- super(pluginName, getPluginVersion(srcFile), loadScriptClasses(srcFile, scriptEngine), Plugin.ApiType.PLUGIN);
+ ScalaPluginScriptEngine scriptEngine) throws InvalidPluginException {
+ super(pluginName, getPluginVersion(srcFile), loadScriptClasses(srcFile,
+ scriptEngine), Plugin.ApiType.PLUGIN);
+
+ this.staticResourcesPath = srcFile;
}
private static String getPluginVersion(File srcFile) {
String srcFileName = srcFile.getName();
int startPos = srcFileName.lastIndexOf('-');
- if(startPos == -1) {
+ if (startPos == -1) {
return "0";
}
int endPos = srcFileName.lastIndexOf('.');
- return srcFileName.substring(startPos+1, endPos);
+ return srcFileName.substring(startPos + 1, endPos);
}
private static Set<Class<?>> loadScriptClasses(File srcFile,
@@ -54,18 +69,63 @@
}
@Override
- public <T> Optional<T> getResource(String resourcePath, Class<? extends T> resourceClass) {
- return Optional.absent();
+ public Optional<PluginEntry> getEntry(String resourcePath) {
+ File resourceFile = getResourceFile(resourcePath);
+ if (resourceFile.exists() && resourceFile.length() > 0) {
+ return resourceOf(resourcePath);
+ } else {
+ return Optional.absent();
+ }
+ }
+
+ private Optional<PluginEntry> resourceOf(String resourcePath) {
+ File file = getResourceFile(resourcePath);
+ if (file.exists() && file.length() > 0) {
+ return Optional.of(new PluginEntry(resourcePath, file.lastModified(), file
+ .length()));
+ } else {
+ return Optional.absent();
+ }
+ }
+
+ private File getResourceFile(String resourcePath) {
+ File resourceFile = new File(staticResourcesPath, resourcePath);
+ return resourceFile;
}
@Override
- public Optional<InputStream> getResourceInputStream(String resourcePath)
+ public InputStream getInputStream(PluginEntry entry)
throws IOException {
- return Optional.absent();
+ return new FileInputStream(getResourceFile(entry.getName()));
}
@Override
- public <T> Enumeration<T> resources(Class<? extends T> resourceClass) {
- return Collections.emptyEnumeration();
+ public Enumeration<PluginEntry> entries() {
+ final List<PluginEntry> resourcesList = Lists.newArrayList();
+ try {
+ Files.walkFileTree(staticResourcesPath.toPath(),
+ EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
+ new SimpleFileVisitor<Path>() {
+ private int basicPathLength = staticResourcesPath.getAbsolutePath()
+ .length();
+
+ @Override
+ public FileVisitResult visitFile(Path path,
+ BasicFileAttributes attrs) throws IOException {
+ Optional<PluginEntry> resource = resourceOf(relativePathOf(path));
+ if (resource.isPresent()) {
+ resourcesList.add(resource.get());
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ private String relativePathOf(Path path) {
+ return path.toFile().getAbsolutePath().substring(basicPathLength);
+ }
+ });
+ } catch (IOException e) {
+ new IllegalArgumentException("Cannot scan resource files in plugin", e);
+ }
+ return Collections.enumeration(resourcesList);
}
}