mirror of
https://github.com/essentials/Essentials.git
synced 2025-08-17 20:11:21 +02:00
Add support for zipped User / Warp files
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -32,3 +32,4 @@ manifest.mf
|
|||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
EssentialsRelease/
|
EssentialsRelease/
|
||||||
|
/Essentials/dependency-reduced-pom.xml
|
@@ -21,7 +21,17 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<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>
|
</dependency>
|
||||||
<!-- Test Depends -->
|
<!-- Test Depends -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -63,4 +73,37 @@
|
|||||||
<version>1.2</version>
|
<version>1.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</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>
|
</project>
|
||||||
|
@@ -5,16 +5,23 @@ import com.google.common.cache.CacheBuilder;
|
|||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentSkipListMap;
|
||||||
import java.util.concurrent.ConcurrentSkipListSet;
|
import java.util.concurrent.ConcurrentSkipListSet;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import net.ess3.api.IEssentials;
|
import net.ess3.api.IEssentials;
|
||||||
import net.ess3.api.InvalidNameException;
|
import net.ess3.api.InvalidNameException;
|
||||||
import net.ess3.utils.Util;
|
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>
|
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;
|
private final transient File folder;
|
||||||
protected final transient Cache<String, I> cache = CacheBuilder.newBuilder().softValues().build(this);
|
protected final transient Cache<String, I> cache = CacheBuilder.newBuilder().softValues().build(this);
|
||||||
protected final transient ConcurrentSkipListSet<String> keys = new ConcurrentSkipListSet<String>();
|
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)
|
public StorageObjectMap(final IEssentials ess, final String folderName)
|
||||||
{
|
{
|
||||||
@@ -51,20 +59,77 @@ public abstract class StorageObjectMap<I> extends CacheLoader<String, I> impleme
|
|||||||
cache.invalidateAll();
|
cache.invalidateAll();
|
||||||
for (String string : folder.list())
|
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
|
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
|
@Override
|
||||||
public void removeObject(final String name) throws InvalidNameException
|
public void removeObject(final String name) throws InvalidNameException
|
||||||
{
|
{
|
||||||
keys.remove(name.toLowerCase(Locale.ENGLISH));
|
String lowerCaseName = name.toLowerCase(Locale.ENGLISH);
|
||||||
cache.invalidate(name.toLowerCase(Locale.ENGLISH));
|
keys.remove(lowerCaseName);
|
||||||
|
cache.invalidate(lowerCaseName);
|
||||||
final File file = getStorageFile(name);
|
final File file = getStorageFile(name);
|
||||||
if (file.exists())
|
if (file.exists())
|
||||||
{
|
{
|
||||||
file.delete();
|
file.delete();
|
||||||
}
|
}
|
||||||
|
if (zippedfiles.containsKey(lowerCaseName))
|
||||||
|
{
|
||||||
|
zippedfiles.put(lowerCaseName, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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));
|
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
|
@Override
|
||||||
@@ -134,4 +211,44 @@ public abstract class StorageObjectMap<I> extends CacheLoader<String, I> impleme
|
|||||||
{
|
{
|
||||||
loadAllObjectsAsync();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@ import java.util.List;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import net.ess3.api.IPlugin;
|
import net.ess3.api.IPlugin;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
@@ -82,17 +83,8 @@ public abstract class EssentialsTest extends TestCase
|
|||||||
plugin = mock(IPlugin.class);
|
plugin = mock(IPlugin.class);
|
||||||
|
|
||||||
|
|
||||||
File tmp;
|
File folder = FileUtils.getTempDirectory();
|
||||||
try
|
when(plugin.getDataFolder()).thenReturn(folder);
|
||||||
{
|
|
||||||
tmp = File.createTempFile("ess", "tmp");
|
|
||||||
}
|
|
||||||
catch (IOException ex)
|
|
||||||
{
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
tmp.deleteOnExit();
|
|
||||||
when(plugin.getDataFolder()).thenReturn(tmp.getParentFile());
|
|
||||||
when(world.getName()).thenReturn("world");
|
when(world.getName()).thenReturn("world");
|
||||||
|
|
||||||
ess = new Essentials(server, logger, plugin);
|
ess = new Essentials(server, logger, plugin);
|
||||||
|
Reference in New Issue
Block a user