Browse Source

Unzip plugin zip file in loadPluginFromPath() (#140)

pull/73/merge
Jan Høydahl 8 years ago committed by Decebal Suiu
parent
commit
39ef17a075
  1. 1
      pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java
  2. 19
      pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java
  3. 41
      pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginRepository.java
  4. 47
      pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java
  5. 65
      pf4j/src/test/java/ro/fortsoft/pf4j/util/FileUtilsTest.java

1
pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java

@ -16,7 +16,6 @@
package ro.fortsoft.pf4j; package ro.fortsoft.pf4j;
import com.github.zafarkhaja.semver.Version; import com.github.zafarkhaja.semver.Version;
import com.github.zafarkhaja.semver.expr.Expression;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import ro.fortsoft.pf4j.util.StringUtils; import ro.fortsoft.pf4j.util.StringUtils;

19
pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java

@ -17,6 +17,7 @@ package ro.fortsoft.pf4j;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import ro.fortsoft.pf4j.util.FileUtils;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
@ -106,4 +107,22 @@ public class DefaultPluginManager extends AbstractPluginManager {
log.info("PF4J version {} in '{}' mode", getVersion(), getRuntimeMode()); log.info("PF4J version {} in '{}' mode", getVersion(), getRuntimeMode());
} }
/**
* Load a plugin from disk. If the path is a zip file, first unpack
* @param pluginPath plugin location on disk
* @return PluginWrapper for the loaded plugin or null if not loaded
* @throws PluginException if problems during load
*/
@Override
protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException {
// First unzip any ZIP files
try {
pluginPath = FileUtils.expandIfZip(pluginPath);
} catch (Exception e) {
log.warn("Failed to unzip " + pluginPath, e);
return null;
}
return super.loadPluginFromPath(pluginPath);
}
} }

41
pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginRepository.java

@ -24,7 +24,6 @@ import ro.fortsoft.pf4j.util.HiddenFilter;
import ro.fortsoft.pf4j.util.NameFileFilter; import ro.fortsoft.pf4j.util.NameFileFilter;
import ro.fortsoft.pf4j.util.NotFileFilter; import ro.fortsoft.pf4j.util.NotFileFilter;
import ro.fortsoft.pf4j.util.OrFileFilter; import ro.fortsoft.pf4j.util.OrFileFilter;
import ro.fortsoft.pf4j.util.Unzip;
import ro.fortsoft.pf4j.util.ZipFileFilter; import ro.fortsoft.pf4j.util.ZipFileFilter;
import java.io.File; import java.io.File;
@ -55,7 +54,7 @@ public class DefaultPluginRepository extends BasePluginRepository {
if ((pluginZips != null) && pluginZips.length > 0) { if ((pluginZips != null) && pluginZips.length > 0) {
for (File pluginZip : pluginZips) { for (File pluginZip : pluginZips) {
try { try {
expandPluginZip(pluginZip); FileUtils.expandIfZip(pluginZip.toPath());
} catch (IOException e) { } catch (IOException e) {
log.error("Cannot expand plugin zip '{}'", pluginZip); log.error("Cannot expand plugin zip '{}'", pluginZip);
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
@ -81,42 +80,4 @@ public class DefaultPluginRepository extends BasePluginRepository {
return hiddenPluginFilter; return hiddenPluginFilter;
} }
/**
* Unzip a plugin zip file in a directory that has the same name as the zip file
* and it's relative to {@code pluginsRoot}.
* For example if the zip file is {@code my-plugin.zip} then the resulted directory
* is {@code my-plugin}.
*
* @param pluginZip
* @return
* @throws IOException
*/
private File expandPluginZip(File pluginZip) throws IOException {
String fileName = pluginZip.getName();
long pluginZipDate = pluginZip.lastModified();
String pluginName = fileName.substring(0, fileName.length() - 4);
File pluginDirectory = pluginsRoot.resolve(pluginName).toFile();
// check if exists root or the '.zip' file is "newer" than root
if (!pluginDirectory.exists() || (pluginZipDate > pluginDirectory.lastModified())) {
log.debug("Expand plugin zip '{}' in '{}'", pluginZip, pluginDirectory);
// do not overwrite an old version, remove it
if (pluginDirectory.exists()) {
FileUtils.delete(pluginDirectory.toPath());
}
// create root for plugin
pluginDirectory.mkdirs();
// expand '.zip' file
Unzip unzip = new Unzip();
unzip.setSource(pluginZip);
unzip.setDestination(pluginDirectory);
unzip.extract();
}
return pluginDirectory;
}
} }

47
pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java

@ -15,13 +15,18 @@
*/ */
package ro.fortsoft.pf4j.util; package ro.fortsoft.pf4j.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*; import java.io.*;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor; import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -29,8 +34,11 @@ import java.util.List;
* @author Decebal Suiu * @author Decebal Suiu
*/ */
public class FileUtils { public class FileUtils {
private static final Logger log = LoggerFactory.getLogger(FileUtils.class);
private static final List<String> ZIP_EXTENSIONS = Arrays.asList(".zip", ".ZIP", ".Zip");
public static List<String> readLines(File file, boolean ignoreComments) throws IOException { public static List<String> readLines(File file, boolean ignoreComments) throws IOException {
if (!file.exists() || !file.isFile()) { if (!file.exists() || !file.isFile()) {
return new ArrayList<>(); return new ArrayList<>();
} }
@ -147,4 +155,41 @@ public class FileUtils {
Files.delete(path); Files.delete(path);
} catch (IOException ignored) { } } catch (IOException ignored) { }
} }
/**
* Unzip a zip file in a directory that has the same name as the zip file.
* For example if the zip file is {@code my-plugin.zip} then the resulted directory
* is {@code my-plugin}.
*
* @param filePath the file to evaluate
* @return Path of unzipped folder or original path if this was not a zip file
* @throws IOException on error
*/
public static Path expandIfZip(Path filePath) throws IOException {
String fileName = filePath.getFileName().toString();
if (!ZIP_EXTENSIONS.contains(fileName.substring(fileName.lastIndexOf(".")))) {
return filePath;
}
FileTime pluginZipDate = Files.getLastModifiedTime(filePath);
Path pluginDirectory = filePath.resolveSibling(fileName.substring(0, fileName.lastIndexOf(".")));
if (!Files.exists(pluginDirectory) || pluginZipDate.compareTo(Files.getLastModifiedTime(pluginDirectory)) > 0) {
// do not overwrite an old version, remove it
if (Files.exists(pluginDirectory)) {
FileUtils.delete(pluginDirectory);
}
// create root for plugin
Files.createDirectories(pluginDirectory);
// expand '.zip' file
Unzip unzip = new Unzip();
unzip.setSource(filePath.toFile());
unzip.setDestination(pluginDirectory.toFile());
unzip.extract();
log.info("Expanded plugin zip '{}' in '{}'", filePath.getFileName(), pluginDirectory.getFileName());
}
return pluginDirectory;
}
} }

65
pf4j/src/test/java/ro/fortsoft/pf4j/util/FileUtilsTest.java

@ -0,0 +1,65 @@
/*
* Copyright 2017 Decebal Suiu
*
* 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 ro.fortsoft.pf4j.util;
import org.junit.Before;
import org.junit.Test;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.nio.file.*;
import java.util.Collections;
import static org.junit.Assert.*;
public class FileUtilsTest {
private Path zipFile;
private Path tmpDir;
private Path propsFile;
@Before
public void setup() throws IOException {
tmpDir = Files.createTempDirectory("pf4j-test");
tmpDir.toFile().deleteOnExit();
zipFile = tmpDir.resolve("my.zip").toAbsolutePath();
propsFile = tmpDir.resolve("plugin.properties");
URI file = URI.create("jar:file:"+zipFile.toString());
try (FileSystem zipfs = FileSystems.newFileSystem(file, Collections.singletonMap("create", "true"))) {
Path propsInZip = zipfs.getPath("/plugin.properties");
BufferedWriter br = new BufferedWriter(new FileWriter(propsFile.toString()));
br.write("plugin.id=test");
br.newLine();
br.write("plugin.version=1.2.3");
br.newLine();
br.write("plugin.class=foo.bar");
br.close();
Files.move(propsFile, propsInZip);
}
}
@Test
public void expandIfZip() throws Exception {
Path unzipped = FileUtils.expandIfZip(zipFile);
assertEquals(tmpDir.resolve("my"), unzipped);
assertTrue(Files.exists(tmpDir.resolve("my/plugin.properties")));
// Non-zip file remains unchanged
assertEquals(propsFile, FileUtils.expandIfZip(propsFile));
}
}
Loading…
Cancel
Save