diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Essentials/pom.xml | 45 | ||||
-rw-r--r-- | Essentials/src/net/ess3/storage/StorageObjectMap.java | 135 | ||||
-rw-r--r-- | Essentials/test/net/ess3/EssentialsTest.java | 14 |
4 files changed, 175 insertions, 22 deletions
diff --git a/.gitignore b/.gitignore index 164936d32..ed9bf34b0 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ manifest.mf *.iws .idea/ -EssentialsRelease/
\ No newline at end of file +EssentialsRelease/ +/Essentials/dependency-reduced-pom.xml
\ No newline at end of file diff --git a/Essentials/pom.xml b/Essentials/pom.xml index f3fd665ff..75b7823be 100644 --- a/Essentials/pom.xml +++ b/Essentials/pom.xml @@ -21,7 +21,17 @@ <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> - <version>0.11.4</version> + <version>0.11.6</version> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.4</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-compress</artifactId> + <version>1.4.1</version> </dependency> <!-- Test Depends --> <dependency> @@ -63,4 +73,37 @@ <version>1.2</version> </dependency> </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>2.0</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <artifactSet> + <includes> + <include>commons-io:*</include> + <include>org.apache.commons:*</include> + </includes> + </artifactSet> + <minimizeJar>true</minimizeJar> + <relocations> + <relocation> + <pattern>org.apache.commons</pattern> + <shadedPattern>net.ess3.commons</shadedPattern> + </relocation> + </relocations> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> </project> diff --git a/Essentials/src/net/ess3/storage/StorageObjectMap.java b/Essentials/src/net/ess3/storage/StorageObjectMap.java index bec082e31..945beb06a 100644 --- a/Essentials/src/net/ess3/storage/StorageObjectMap.java +++ b/Essentials/src/net/ess3/storage/StorageObjectMap.java @@ -5,16 +5,23 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.util.concurrent.UncheckedExecutionException; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.util.Collections; +import java.util.Enumeration; import java.util.Locale; import java.util.Set; +import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.ExecutionException; import java.util.logging.Level; +import java.util.regex.Pattern; import net.ess3.api.IEssentials; import net.ess3.api.InvalidNameException; import net.ess3.utils.Util; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; +import org.apache.commons.io.IOUtils; public abstract class StorageObjectMap<I> extends CacheLoader<String, I> implements IStorageObjectMap<I> @@ -23,6 +30,7 @@ public abstract class StorageObjectMap<I> extends CacheLoader<String, I> impleme private final transient File folder; protected final transient Cache<String, I> cache = CacheBuilder.newBuilder().softValues().build(this); protected final transient ConcurrentSkipListSet<String> keys = new ConcurrentSkipListSet<String>(); + protected final transient ConcurrentSkipListMap<String, File> zippedfiles = new ConcurrentSkipListMap<String, File>(); public StorageObjectMap(final IEssentials ess, final String folderName) { @@ -51,20 +59,77 @@ public abstract class StorageObjectMap<I> extends CacheLoader<String, I> impleme cache.invalidateAll(); for (String string : folder.list()) { + final File file = new File(folder, string); + if (!file.isFile() || !file.canRead()) + { + continue; + } + if (string.endsWith(".yml")) + { + addFileToKeys(string.substring(0, string.length() - 4)); + } + if (string.endsWith(".zip")) + { + addZipFile(file); + } + } + } + + private void addFileToKeys(String filename) + { + try + { + + final String name = Util.decodeFileName(filename); + keys.add(name.toLowerCase(Locale.ENGLISH)); + + } + catch (InvalidNameException ex) + { + ess.getLogger().log(Level.WARNING, "Invalid filename: " + filename, ex); + } + } + + private final Pattern zipCheck = Pattern.compile("^[a-zA-Z0-9-]+\\.yml$"); + + private void addZipFile(File file) + { + try + { + ZipFile zipFile = new ZipFile(file); try { - if (!string.endsWith(".yml")) + Enumeration<ZipArchiveEntry> entries = zipFile.getEntriesInPhysicalOrder(); + while (entries.hasMoreElements()) { - continue; + ZipArchiveEntry entry = entries.nextElement(); + String name = entry.getName(); + if (entry.isDirectory() || entry.getSize() == 0 || !zipCheck.matcher(name).matches()) + { + continue; + } + try + { + String shortName = name.substring(0, name.length() - 4); + addFileToKeys(shortName); + final String decodedName = Util.decodeFileName(shortName).toLowerCase(Locale.ENGLISH); + zippedfiles.put(decodedName, file); + } + catch (InvalidNameException ex) + { + ess.getLogger().log(Level.WARNING, "Invalid filename " + name + " in " + file.getAbsoluteFile(), ex); + } } - final String name = Util.decodeFileName(string.substring(0, string.length() - 4)); - keys.add(name.toLowerCase(Locale.ENGLISH)); } - catch (InvalidNameException ex) + finally { - ess.getLogger().log(Level.WARNING, "Invalid filename: " + string, ex); + zipFile.close(); } } + catch (IOException ex) + { + ess.getLogger().log(Level.WARNING, "Error opening file " + file.getAbsolutePath(), ex); + } } }); } @@ -98,13 +163,18 @@ public abstract class StorageObjectMap<I> extends CacheLoader<String, I> impleme @Override public void removeObject(final String name) throws InvalidNameException { - keys.remove(name.toLowerCase(Locale.ENGLISH)); - cache.invalidate(name.toLowerCase(Locale.ENGLISH)); + String lowerCaseName = name.toLowerCase(Locale.ENGLISH); + keys.remove(lowerCaseName); + cache.invalidate(lowerCaseName); final File file = getStorageFile(name); if (file.exists()) { file.delete(); } + if (zippedfiles.containsKey(lowerCaseName)) + { + zippedfiles.put(lowerCaseName, null); + } } @Override @@ -126,7 +196,14 @@ public abstract class StorageObjectMap<I> extends CacheLoader<String, I> impleme { throw new InvalidNameException(new IOException("Folder does not exists: " + folder)); } - return new File(folder, Util.sanitizeFileName(name) + ".yml"); + String sanitizedFilename = Util.sanitizeFileName(name) + ".yml"; + File file = new File(folder, sanitizedFilename); + + if (!file.exists()) + { + extractFileFromZip(name, sanitizedFilename, file); + } + return file; } @Override @@ -134,4 +211,44 @@ public abstract class StorageObjectMap<I> extends CacheLoader<String, I> impleme { loadAllObjectsAsync(); } + + private void extractFileFromZip(final String name, String sanitizedFilename, File file) + { + String lowerCaseName = name.toLowerCase(Locale.ENGLISH); + File zipFile = zippedfiles.get(lowerCaseName); + if (zipFile != null) + { + try + { + ZipFile zip = new ZipFile(zipFile); + try + { + ZipArchiveEntry entry = zip.getEntry(sanitizedFilename); + if (entry != null) + { + try + { + IOUtils.copy(zip.getInputStream(entry), new FileOutputStream(file)); + } + catch (IOException ex) + { + ess.getLogger().log(Level.WARNING, "Failed to write file: " + file.getAbsolutePath(), ex); + } + } + else + { + ess.getLogger().log(Level.WARNING, "File " + file.getAbsolutePath() + " not found in zip file " + zipFile.getAbsolutePath()); + } + } + finally + { + zip.close(); + } + } + catch (IOException ex) + { + ess.getLogger().log(Level.WARNING, "File " + file.getAbsolutePath() + " could not be extracted from " + zipFile.getAbsolutePath(), ex); + } + } + } } diff --git a/Essentials/test/net/ess3/EssentialsTest.java b/Essentials/test/net/ess3/EssentialsTest.java index 547b03f19..5eef12025 100644 --- a/Essentials/test/net/ess3/EssentialsTest.java +++ b/Essentials/test/net/ess3/EssentialsTest.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.logging.Logger; import junit.framework.TestCase; import net.ess3.api.IPlugin; +import org.apache.commons.io.FileUtils; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.Server; @@ -82,17 +83,8 @@ public abstract class EssentialsTest extends TestCase plugin = mock(IPlugin.class); - File tmp; - try - { - tmp = File.createTempFile("ess", "tmp"); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - tmp.deleteOnExit(); - when(plugin.getDataFolder()).thenReturn(tmp.getParentFile()); + File folder = FileUtils.getTempDirectory(); + when(plugin.getDataFolder()).thenReturn(folder); when(world.getName()).thenReturn("world"); ess = new Essentials(server, logger, plugin); |