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 @@
org.projectlombok
lombok
- 0.11.4
+ 0.11.6
+
+
+ commons-io
+ commons-io
+ 2.4
+
+
+ org.apache.commons
+ commons-compress
+ 1.4.1
@@ -63,4 +73,37 @@
1.2
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.0
+
+
+ package
+
+ shade
+
+
+
+
+ commons-io:*
+ org.apache.commons:*
+
+
+ true
+
+
+ org.apache.commons
+ net.ess3.commons
+
+
+
+
+
+
+
+
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 extends CacheLoader implements IStorageObjectMap
@@ -23,6 +30,7 @@ public abstract class StorageObjectMap extends CacheLoader impleme
private final transient File folder;
protected final transient Cache cache = CacheBuilder.newBuilder().softValues().build(this);
protected final transient ConcurrentSkipListSet keys = new ConcurrentSkipListSet();
+ protected final transient ConcurrentSkipListMap zippedfiles = new ConcurrentSkipListMap();
public StorageObjectMap(final IEssentials ess, final String folderName)
{
@@ -51,20 +59,77 @@ public abstract class StorageObjectMap extends CacheLoader 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 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 extends CacheLoader 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 extends CacheLoader 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 extends CacheLoader 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);