#41: Quota increase/decrease when file is added/delete

+ java.nio-ization
This commit is contained in:
jendib 2015-11-29 19:42:49 +01:00
parent 1466fb4d6c
commit 90a4949d76
14 changed files with 261 additions and 101 deletions

View File

@ -228,7 +228,7 @@ public class UserDao {
Map<String, Object> parameterMap = new HashMap<String, Object>(); Map<String, Object> parameterMap = new HashMap<String, Object>();
List<String> criteriaList = new ArrayList<String>(); List<String> criteriaList = new ArrayList<String>();
StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3"); StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3, u.USE_STORAGECURRENT_N as c4, u.USE_STORAGEQUOTA_N as c5");
sb.append(" from T_USER u "); sb.append(" from T_USER u ");
// Add search criterias // Add search criterias
@ -257,6 +257,8 @@ public class UserDao {
userDto.setUsername((String) o[i++]); userDto.setUsername((String) o[i++]);
userDto.setEmail((String) o[i++]); userDto.setEmail((String) o[i++]);
userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime()); userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
userDto.setStorageCurrent(((Number) o[i++]).longValue());
userDto.setStorageQuota(((Number) o[i++]).longValue());
userDtoList.add(userDto); userDtoList.add(userDto);
} }
paginatedList.setResultList(userDtoList); paginatedList.setResultList(userDtoList);

View File

@ -1,5 +1,6 @@
package com.sismics.docs.core.dao.jpa.dto; package com.sismics.docs.core.dao.jpa.dto;
/** /**
* User DTO. * User DTO.
* *
@ -26,6 +27,16 @@ public class UserDto {
*/ */
private Long createTimestamp; private Long createTimestamp;
/**
* Storage quota.
*/
private Long storageQuota;
/**
* Storage current usage.
*/
private Long storageCurrent;
/** /**
* Getter of id. * Getter of id.
* *
@ -88,6 +99,22 @@ public class UserDto {
public Long getCreateTimestamp() { public Long getCreateTimestamp() {
return createTimestamp; return createTimestamp;
} }
public Long getStorageQuota() {
return storageQuota;
}
public void setStorageQuota(Long storageQuota) {
this.storageQuota = storageQuota;
}
public Long getStorageCurrent() {
return storageCurrent;
}
public void setStorageCurrent(Long storageCurrent) {
this.storageCurrent = storageCurrent;
}
/** /**
* Setter of createTimestamp. * Setter of createTimestamp.

View File

@ -1,7 +1,7 @@
package com.sismics.docs.core.service; package com.sismics.docs.core.service;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.DirectoryReader;
@ -56,10 +56,10 @@ public class IndexingService extends AbstractScheduledService {
directory = new RAMDirectory(); directory = new RAMDirectory();
log.info("Using RAM Lucene storage"); log.info("Using RAM Lucene storage");
} else if (luceneStorageConfig.equals(Constants.LUCENE_DIRECTORY_STORAGE_FILE)) { } else if (luceneStorageConfig.equals(Constants.LUCENE_DIRECTORY_STORAGE_FILE)) {
File luceneDirectory = DirectoryUtil.getLuceneDirectory(); Path luceneDirectory = DirectoryUtil.getLuceneDirectory();
log.info("Using file Lucene storage: {}", luceneDirectory); log.info("Using file Lucene storage: {}", luceneDirectory);
try { try {
directory = new SimpleFSDirectory(luceneDirectory, new SimpleFSLockFactory()); directory = new SimpleFSDirectory(luceneDirectory.toFile(), new SimpleFSLockFactory());
} catch (IOException e) { } catch (IOException e) {
log.error("Error initializing Lucene index", e); log.error("Error initializing Lucene index", e);
} }

View File

@ -1,6 +1,9 @@
package com.sismics.docs.core.util; package com.sismics.docs.core.util;
import java.io.File; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
@ -17,27 +20,31 @@ public class DirectoryUtil {
* *
* @return Base data directory * @return Base data directory
*/ */
public static File getBaseDataDirectory() { public static Path getBaseDataDirectory() {
File baseDataDir = null; Path baseDataDir = null;
if (StringUtils.isNotBlank(EnvironmentUtil.getDocsHome())) { if (StringUtils.isNotBlank(EnvironmentUtil.getDocsHome())) {
// If the docs.home property is set then use it // If the docs.home property is set then use it
baseDataDir = new File(EnvironmentUtil.getDocsHome()); baseDataDir = Paths.get(EnvironmentUtil.getDocsHome());
} else if (EnvironmentUtil.isUnitTest()) { } else if (EnvironmentUtil.isUnitTest()) {
// For unit testing, use a temporary directory // For unit testing, use a temporary directory
baseDataDir = new File(System.getProperty("java.io.tmpdir")); baseDataDir = Paths.get(System.getProperty("java.io.tmpdir"));
} else { } else {
// We are in a webapp environment and nothing is specified, use the default directory for this OS // We are in a webapp environment and nothing is specified, use the default directory for this OS
if (EnvironmentUtil.isUnix()) { if (EnvironmentUtil.isUnix()) {
baseDataDir = new File("/var/docs"); baseDataDir = Paths.get("/var/docs");
} if (EnvironmentUtil.isWindows()) { } if (EnvironmentUtil.isWindows()) {
baseDataDir = new File(EnvironmentUtil.getWindowsAppData() + "\\Sismics\\Docs"); baseDataDir = Paths.get(EnvironmentUtil.getWindowsAppData() + "\\Sismics\\Docs");
} else if (EnvironmentUtil.isMacOs()) { } else if (EnvironmentUtil.isMacOs()) {
baseDataDir = new File(EnvironmentUtil.getMacOsUserHome() + "/Library/Sismics/Docs"); baseDataDir = Paths.get(EnvironmentUtil.getMacOsUserHome() + "/Library/Sismics/Docs");
} }
} }
if (baseDataDir != null && !baseDataDir.isDirectory()) { if (baseDataDir != null && !Files.isDirectory(baseDataDir)) {
baseDataDir.mkdirs(); try {
Files.createDirectories(baseDataDir);
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
return baseDataDir; return baseDataDir;
@ -48,7 +55,7 @@ public class DirectoryUtil {
* *
* @return Database directory. * @return Database directory.
*/ */
public static File getDbDirectory() { public static Path getDbDirectory() {
return getDataSubDirectory("db"); return getDataSubDirectory("db");
} }
@ -57,7 +64,7 @@ public class DirectoryUtil {
* *
* @return Lucene indexes directory. * @return Lucene indexes directory.
*/ */
public static File getLuceneDirectory() { public static Path getLuceneDirectory() {
return getDataSubDirectory("lucene"); return getDataSubDirectory("lucene");
} }
@ -66,7 +73,7 @@ public class DirectoryUtil {
* *
* @return Storage directory. * @return Storage directory.
*/ */
public static File getStorageDirectory() { public static Path getStorageDirectory() {
return getDataSubDirectory("storage"); return getDataSubDirectory("storage");
} }
@ -75,7 +82,7 @@ public class DirectoryUtil {
* *
* @return Log directory. * @return Log directory.
*/ */
public static File getLogDirectory() { public static Path getLogDirectory() {
return getDataSubDirectory("log"); return getDataSubDirectory("log");
} }
@ -84,11 +91,15 @@ public class DirectoryUtil {
* *
* @return Subdirectory * @return Subdirectory
*/ */
private static File getDataSubDirectory(String subdirectory) { private static Path getDataSubDirectory(String subdirectory) {
File baseDataDir = getBaseDataDirectory(); Path baseDataDir = getBaseDataDirectory();
File directory = new File(baseDataDir.getPath() + File.separator + subdirectory); Path directory = baseDataDir.resolve(subdirectory);
if (!directory.isDirectory()) { if (!Files.isDirectory(directory)) {
directory.mkdirs(); try {
Files.createDirectories(directory);
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
return directory; return directory;
} }

View File

@ -1,13 +1,11 @@
package com.sismics.docs.core.util; package com.sismics.docs.core.util;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.CipherInputStream; import javax.crypto.CipherInputStream;
@ -132,7 +130,7 @@ public class FileUtil {
*/ */
public static void save(InputStream inputStream, File file, String privateKey) throws Exception { public static void save(InputStream inputStream, File file, String privateKey) throws Exception {
Cipher cipher = EncryptionUtil.getEncryptionCipher(privateKey); Cipher cipher = EncryptionUtil.getEncryptionCipher(privateKey);
Path path = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId()); Path path = DirectoryUtil.getStorageDirectory().resolve(file.getId());
Files.copy(new CipherInputStream(inputStream, cipher), path); Files.copy(new CipherInputStream(inputStream, cipher), path);
// Generate file variations // Generate file variations
@ -172,21 +170,15 @@ public class FileUtil {
image.flush(); image.flush();
// Write "web" encrypted image // Write "web" encrypted image
java.io.File outputFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_web").toFile(); Path outputFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_web");
OutputStream outputStream = new CipherOutputStream(new FileOutputStream(outputFile), cipher); try (OutputStream outputStream = new CipherOutputStream(Files.newOutputStream(outputFile), cipher)) {
try {
ImageUtil.writeJpeg(web, outputStream); ImageUtil.writeJpeg(web, outputStream);
} finally {
outputStream.close();
} }
// Write "thumb" encrypted image // Write "thumb" encrypted image
outputFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_thumb").toFile(); outputFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_thumb");
outputStream = new CipherOutputStream(new FileOutputStream(outputFile), cipher); try (OutputStream outputStream = new CipherOutputStream(Files.newOutputStream(outputFile), cipher)) {
try {
ImageUtil.writeJpeg(thumbnail, outputStream); ImageUtil.writeJpeg(thumbnail, outputStream);
} finally {
outputStream.close();
} }
} }
} }
@ -195,20 +187,21 @@ public class FileUtil {
* Remove a file from the storage filesystem. * Remove a file from the storage filesystem.
* *
* @param file File to delete * @param file File to delete
* @throws IOException
*/ */
public static void delete(File file) { public static void delete(File file) throws IOException {
java.io.File storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId()).toFile(); Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
java.io.File webFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_web").toFile(); Path webFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_web");
java.io.File thumbnailFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_thumb").toFile(); Path thumbnailFile = DirectoryUtil.getStorageDirectory().resolve(file.getId() + "_thumb");
if (storedFile.exists()) { if (Files.exists(storedFile)) {
storedFile.delete(); Files.delete(storedFile);
} }
if (webFile.exists()) { if (Files.exists(webFile)) {
webFile.delete(); Files.delete(webFile);
} }
if (thumbnailFile.exists()) { if (Files.exists(thumbnailFile)) {
thumbnailFile.delete(); Files.delete(thumbnailFile);
} }
} }
} }

View File

@ -1,6 +1,16 @@
package com.sismics.util.jpa; package com.sismics.util.jpa;
import com.sismics.docs.core.util.DirectoryUtil; import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
@ -8,15 +18,7 @@ import org.hibernate.service.ServiceRegistryBuilder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.persistence.EntityManagerFactory; import com.sismics.docs.core.util.DirectoryUtil;
import javax.persistence.Persistence;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/** /**
* Entity manager factory. * Entity manager factory.
@ -79,8 +81,8 @@ public final class EMF {
log.info("Configuring EntityManager from environment parameters"); log.info("Configuring EntityManager from environment parameters");
Map<Object, Object> props = new HashMap<Object, Object>(); Map<Object, Object> props = new HashMap<Object, Object>();
props.put("hibernate.connection.driver_class", "org.h2.Driver"); props.put("hibernate.connection.driver_class", "org.h2.Driver");
File dbDirectory = DirectoryUtil.getDbDirectory(); Path dbDirectory = DirectoryUtil.getDbDirectory();
String dbFile = dbDirectory.getAbsoluteFile() + File.separator + "docs"; String dbFile = dbDirectory.resolve("docs").toAbsolutePath().toString();
props.put("hibernate.connection.url", "jdbc:h2:file:" + dbFile + ";CACHE_SIZE=65536"); props.put("hibernate.connection.url", "jdbc:h2:file:" + dbFile + ";CACHE_SIZE=65536");
props.put("hibernate.connection.username", "sa"); props.put("hibernate.connection.username", "sa");
props.put("hibernate.hbm2ddl.auto", "none"); props.put("hibernate.hbm2ddl.auto", "none");

View File

@ -1,6 +1,5 @@
package com.sismics.util.filter; package com.sismics.util.filter;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.text.MessageFormat; import java.text.MessageFormat;
@ -44,20 +43,19 @@ public class RequestContextFilter implements Filter {
if (!filterConfig.getServletContext().getServerInfo().startsWith("Grizzly")) { if (!filterConfig.getServletContext().getServerInfo().startsWith("Grizzly")) {
EnvironmentUtil.setWebappContext(true); EnvironmentUtil.setWebappContext(true);
} }
File baseDataDirectory = null;
try { try {
baseDataDirectory = DirectoryUtil.getBaseDataDirectory(); if (log.isInfoEnabled()) {
log.info(MessageFormat.format("Using base data directory: {0}", DirectoryUtil.getBaseDataDirectory()));
}
} catch (Exception e) { } catch (Exception e) {
log.error("Error initializing base data directory", e); log.error("Error initializing base data directory", e);
} }
if (log.isInfoEnabled()) {
log.info(MessageFormat.format("Using base data directory: {0}", baseDataDirectory.toString()));
}
// Initialize file logger // Initialize file logger
RollingFileAppender fileAppender = new RollingFileAppender(); RollingFileAppender fileAppender = new RollingFileAppender();
fileAppender.setName("FILE"); fileAppender.setName("FILE");
fileAppender.setFile(DirectoryUtil.getLogDirectory() + File.separator + "docs.log"); fileAppender.setFile(DirectoryUtil.getLogDirectory().resolve("docs.log").toString());
fileAppender.setLayout(new PatternLayout("%d{DATE} %p %l %m %n")); fileAppender.setLayout(new PatternLayout("%d{DATE} %p %l %m %n"));
fileAppender.setThreshold(Level.INFO); fileAppender.setThreshold(Level.INFO);
fileAppender.setAppend(true); fileAppender.setAppend(true);

View File

@ -1,8 +1,6 @@
package com.sismics.docs.rest; package com.sismics.docs.rest;
import java.io.File;
import java.net.URI; import java.net.URI;
import java.net.URLDecoder;
import javax.ws.rs.core.Application; import javax.ws.rs.core.Application;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
@ -63,8 +61,7 @@ public abstract class BaseJerseyTest extends JerseyTest {
clientUtil = new ClientUtil(target()); clientUtil = new ClientUtil(target());
String httpRoot = URLDecoder.decode(new File(getClass().getResource("/").getFile()).getAbsolutePath(), "utf-8"); httpServer = HttpServer.createSimpleServer(getClass().getResource("/").getFile(), "localhost", getPort());
httpServer = HttpServer.createSimpleServer(httpRoot, "localhost", getPort());
WebappContext context = new WebappContext("GrizzlyContext", "/docs"); WebappContext context = new WebappContext("GrizzlyContext", "/docs");
context.addFilter("requestContextFilter", RequestContextFilter.class) context.addFilter("requestContextFilter", RequestContextFilter.class)
.addMappingForUrlPatterns(null, "/*"); .addMappingForUrlPatterns(null, "/*");

View File

@ -1,5 +1,8 @@
package com.sismics.docs.rest.resource; package com.sismics.docs.rest.resource;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -165,13 +168,16 @@ public class AppResource extends BaseResource {
} }
// Check if each stored file is valid // Check if each stored file is valid
java.io.File[] storedFileList = DirectoryUtil.getStorageDirectory().listFiles(); try (DirectoryStream<java.nio.file.Path> storedFileList = Files.newDirectoryStream(DirectoryUtil.getStorageDirectory())) {
for (java.io.File storedFile : storedFileList) { for (java.nio.file.Path storedFile : storedFileList) {
String fileName = storedFile.getName(); String fileName = storedFile.getFileName().toString();
String[] fileNameArray = fileName.split("_"); String[] fileNameArray = fileName.split("_");
if (!fileMap.containsKey(fileNameArray[0])) { if (!fileMap.containsKey(fileNameArray[0])) {
storedFile.delete(); Files.delete(storedFile);
}
} }
} catch (IOException e) {
throw new ServerException("FileError", "Error deleting orphan files", e);
} }
// Always return OK // Always return OK

View File

@ -1,10 +1,10 @@
package com.sismics.docs.rest.resource; package com.sismics.docs.rest.resource;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -123,6 +123,11 @@ public class FileResource extends BaseResource {
throw new ClientException("InvalidFileType", "File type not recognized"); throw new ClientException("InvalidFileType", "File type not recognized");
} }
// Validate quota
if (user.getStorageCurrent() + fileData.length > user.getStorageQuota()) {
throw new ClientException("QuotaReached", "Quota limit reached");
}
try { try {
// Get files of this document // Get files of this document
FileDao fileDao = new FileDao(); FileDao fileDao = new FileDao();
@ -144,6 +149,10 @@ public class FileResource extends BaseResource {
// Save the file // Save the file
FileUtil.save(fileInputStream, file, user.getPrivateKey()); FileUtil.save(fileInputStream, file, user.getPrivateKey());
// Update the user quota
user.setStorageCurrent(user.getStorageCurrent() + fileData.length);
userDao.update(user);
// Raise a new file created event if we have a document // Raise a new file created event if we have a document
if (documentId != null) { if (documentId != null) {
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent(); FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
@ -206,8 +215,8 @@ public class FileResource extends BaseResource {
// Raise a new file created event (it wasn't sent during file creation) // Raise a new file created event (it wasn't sent during file creation)
try { try {
java.io.File storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), id).toFile(); java.nio.file.Path storedFile = DirectoryUtil.getStorageDirectory().resolve(id);
InputStream fileInputStream = new FileInputStream(storedfile); InputStream fileInputStream = Files.newInputStream(storedFile);
final InputStream responseInputStream = EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey()); final InputStream responseInputStream = EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey());
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent(); FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
fileCreatedAsyncEvent.setDocument(document); fileCreatedAsyncEvent.setDocument(document);
@ -341,6 +350,17 @@ public class FileResource extends BaseResource {
// Delete the file // Delete the file
fileDao.delete(file.getId()); fileDao.delete(file.getId());
// Update the user quota
UserDao userDao = new UserDao();
User user = userDao.getById(principal.getId());
java.nio.file.Path storedFile = DirectoryUtil.getStorageDirectory().resolve(id);
try {
user.setStorageCurrent(user.getStorageCurrent() - Files.size(storedFile));
userDao.update(user);
} catch (IOException e) {
// The file doesn't exists on disk, which is weird, but not fatal
}
// Raise a new file deleted event // Raise a new file deleted event
FileDeletedAsyncEvent fileDeletedAsyncEvent = new FileDeletedAsyncEvent(); FileDeletedAsyncEvent fileDeletedAsyncEvent = new FileDeletedAsyncEvent();
fileDeletedAsyncEvent.setFile(file); fileDeletedAsyncEvent.setFile(file);
@ -396,30 +416,33 @@ public class FileResource extends BaseResource {
// Get the stored file // Get the stored file
java.io.File storedfile; java.nio.file.Path storedFile;
String mimeType; String mimeType;
boolean decrypt = false; boolean decrypt = false;
if (size != null) { if (size != null) {
storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId + "_" + size).toFile(); storedFile = DirectoryUtil.getStorageDirectory().resolve(fileId + "_" + size);
mimeType = MimeType.IMAGE_JPEG; // Thumbnails are JPEG mimeType = MimeType.IMAGE_JPEG; // Thumbnails are JPEG
decrypt = true; // Thumbnails are encrypted decrypt = true; // Thumbnails are encrypted
if (!storedfile.exists()) { if (!Files.exists(storedFile)) {
storedfile = new java.io.File(getClass().getResource("/image/file.png").getFile()); storedFile = Paths.get(getClass().getResource("/image/file.png").getFile());
mimeType = MimeType.IMAGE_PNG; mimeType = MimeType.IMAGE_PNG;
decrypt = false; decrypt = false;
} }
} else { } else {
storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId).toFile(); storedFile = DirectoryUtil.getStorageDirectory().resolve(fileId);
mimeType = file.getMimeType(); mimeType = file.getMimeType();
decrypt = true; // Original files are encrypted decrypt = true; // Original files are encrypted
} }
// Stream the output and decrypt it if necessary // Stream the output and decrypt it if necessary
StreamingOutput stream; StreamingOutput stream;
// A file is always encrypted by the creator of it // A file is always encrypted by the creator of it
User user = userDao.getById(file.getUserId()); User user = userDao.getById(file.getUserId());
// Write the decrypted file to the output
try { try {
InputStream fileInputStream = new FileInputStream(storedfile); InputStream fileInputStream = Files.newInputStream(storedFile);
final InputStream responseInputStream = decrypt ? final InputStream responseInputStream = decrypt ?
EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey()) : fileInputStream; EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey()) : fileInputStream;
@ -484,8 +507,8 @@ public class FileResource extends BaseResource {
// Add each file to the ZIP stream // Add each file to the ZIP stream
int index = 0; int index = 0;
for (File file : fileList) { for (File file : fileList) {
java.io.File storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId()).toFile(); java.nio.file.Path storedfile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
InputStream fileInputStream = new FileInputStream(storedfile); InputStream fileInputStream = Files.newInputStream(storedfile);
// Add the decrypted file to the ZIP stream // Add the decrypted file to the ZIP stream
// Files are encrypted by the creator of them // Files are encrypted by the creator of them

View File

@ -489,6 +489,8 @@ public class UserResource extends BaseResource {
.add("id", userDto.getId()) .add("id", userDto.getId())
.add("username", userDto.getUsername()) .add("username", userDto.getUsername())
.add("email", userDto.getEmail()) .add("email", userDto.getEmail())
.add("storage_quota", userDto.getStorageQuota())
.add("storage_current", userDto.getStorageCurrent())
.add("create_date", userDto.getCreateTimestamp())); .add("create_date", userDto.getCreateTimestamp()));
} }

View File

@ -1,7 +1,6 @@
package com.sismics.docs.rest; package com.sismics.docs.rest;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Paths;
import java.util.Date; import java.util.Date;
import javax.json.JsonArray; import javax.json.JsonArray;
@ -237,9 +236,9 @@ public class TestDocumentResource extends BaseJerseyTest {
Assert.assertEquals("ok", json.getString("status")); Assert.assertEquals("ok", json.getString("status"));
// Check that the associated files are deleted from FS // Check that the associated files are deleted from FS
java.io.File storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile(); java.io.File storedFile = DirectoryUtil.getStorageDirectory().resolve(file1Id).toFile();
java.io.File webFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_web").toFile(); java.io.File webFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_web").toFile();
java.io.File thumbnailFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_thumb").toFile(); java.io.File thumbnailFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_thumb").toFile();
Assert.assertFalse(storedFile.exists()); Assert.assertFalse(storedFile.exists());
Assert.assertFalse(webFile.exists()); Assert.assertFalse(webFile.exists());
Assert.assertFalse(thumbnailFile.exists()); Assert.assertFalse(thumbnailFile.exists());

View File

@ -1,9 +1,9 @@
package com.sismics.docs.rest; package com.sismics.docs.rest;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Paths; import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Date; import java.util.Date;
import javax.json.JsonArray; import javax.json.JsonArray;
@ -121,8 +121,8 @@ public class TestFileResource extends BaseJerseyTest {
Assert.assertTrue(fileBytes.length > 0); Assert.assertTrue(fileBytes.length > 0);
// Check that the files are not readable directly from FS // Check that the files are not readable directly from FS
java.io.File storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile(); Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file1Id);
try (InputStream storedFileInputStream = new BufferedInputStream(new FileInputStream(storedFile))) { try (InputStream storedFileInputStream = new BufferedInputStream(Files.newInputStream(storedFile))) {
Assert.assertNull(MimeTypeUtil.guessMimeType(storedFileInputStream)); Assert.assertNull(MimeTypeUtil.guessMimeType(storedFileInputStream));
} }
@ -179,12 +179,12 @@ public class TestFileResource extends BaseJerseyTest {
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus())); Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
// Check that files are deleted from FS // Check that files are deleted from FS
storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile(); storedFile = DirectoryUtil.getStorageDirectory().resolve(file1Id);
java.io.File webFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_web").toFile(); Path webFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_web");
java.io.File thumbnailFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_thumb").toFile(); Path thumbnailFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_thumb");
Assert.assertFalse(storedFile.exists()); Assert.assertFalse(Files.exists(storedFile));
Assert.assertFalse(webFile.exists()); Assert.assertFalse(Files.exists(webFile));
Assert.assertFalse(thumbnailFile.exists()); Assert.assertFalse(Files.exists(thumbnailFile));
// Get all files from a document // Get all files from a document
json = target().path("/file/list") json = target().path("/file/list")
@ -198,7 +198,7 @@ public class TestFileResource extends BaseJerseyTest {
@Test @Test
public void testOrphanFile() throws Exception { public void testOrphanFile() throws Exception {
// Login file1 // Login file2
clientUtil.createUser("file2"); clientUtil.createUser("file2");
String file2AuthenticationToken = clientUtil.login("file2"); String file2AuthenticationToken = clientUtil.login("file2");
@ -280,4 +280,97 @@ public class TestFileResource extends BaseJerseyTest {
.delete(JsonObject.class); .delete(JsonObject.class);
Assert.assertEquals("ok", json.getString("status")); Assert.assertEquals("ok", json.getString("status"));
} }
@Test
public void testQuota() throws Exception {
// Login file_quota
clientUtil.createUser("file_quota");
String fileQuotaAuthenticationToken = clientUtil.login("file_quota");
// Add a file (292641 bytes large)
String file1Id = null;
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
JsonObject json = target()
.register(MultiPartFeature.class)
.path("/file").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
file1Id = json.getString("id");
Assert.assertNotNull(file1Id);
}
}
// Check current quota
JsonObject json = target().path("/user").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
.get(JsonObject.class);
Assert.assertEquals(292641l, json.getJsonNumber("storage_current").longValue());
// Add a file (292641 bytes large)
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
target()
.register(MultiPartFeature.class)
.path("/file").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
}
}
// Check current quota
json = target().path("/user").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
.get(JsonObject.class);
Assert.assertEquals(585282l, json.getJsonNumber("storage_current").longValue());
// Add a file (292641 bytes large)
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
target()
.register(MultiPartFeature.class)
.path("/file").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
}
}
// Check current quota
json = target().path("/user").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
.get(JsonObject.class);
Assert.assertEquals(877923l, json.getJsonNumber("storage_current").longValue());
// Add a file (292641 bytes large)
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
Response response = target()
.register(MultiPartFeature.class)
.path("/file").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
MediaType.MULTIPART_FORM_DATA_TYPE));
Assert.assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
}
}
// Deletes a file
json = target().path("/file/" + file1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
.delete(JsonObject.class);
Assert.assertEquals("ok", json.getString("status"));
// Check current quota
json = target().path("/user").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
.get(JsonObject.class);
Assert.assertEquals(585282l, json.getJsonNumber("storage_current").longValue());
}
} }

View File

@ -48,6 +48,13 @@ public class TestUserResource extends BaseJerseyTest {
.get(JsonObject.class); .get(JsonObject.class);
JsonArray users = json.getJsonArray("users"); JsonArray users = json.getJsonArray("users");
Assert.assertTrue(users.size() > 0); Assert.assertTrue(users.size() > 0);
JsonObject user = users.getJsonObject(0);
Assert.assertNotNull(user.getString("id"));
Assert.assertNotNull(user.getString("username"));
Assert.assertNotNull(user.getString("email"));
Assert.assertNotNull(user.getJsonNumber("storage_quota"));
Assert.assertNotNull(user.getJsonNumber("storage_current"));
Assert.assertNotNull(user.getJsonNumber("create_date"));
// Create a user KO (login length validation) // Create a user KO (login length validation)
Response response = target().path("/user").request() Response response = target().path("/user").request()