package filehandling.storage; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.List; import org.molgenis.core.RuntimeProperty; import org.molgenis.framework.db.Database; import org.molgenis.framework.db.DatabaseException; import org.molgenis.framework.db.QueryRule; import org.molgenis.framework.db.QueryRule.Operator; import org.molgenis.util.DetectOS; import app.servlet.UsedMolgenisOptions; public class StorageHandler { static String RUNTIME_FILE_STORAGE_PATH = "file_storage_path"; static String RUNTIME_FILE_STORAGE_VALIDATED = "file_storage_validated"; Report report; /** * On instantiation, build a simple report of the current state, without * running actual validation yet. * * @param db */ public StorageHandler(Database db) { report = new Report(); RuntimeProperty pathRp = null; RuntimeProperty validRp = null; try { pathRp = getRuntimeProperty(RUNTIME_FILE_STORAGE_PATH, db); validRp = getRuntimeProperty(RUNTIME_FILE_STORAGE_VALIDATED, db); } catch (DatabaseException e) { // tables do not exist (yet) // that's OK, see below } if (pathRp == null && validRp == null) { report.setFileStoragePropsPresent(false); } else { // try-catch not so nice here, this should never fail if the // storage props are set.. but it saves you from a lot of // try-catch in plugin reloads etc. Exception is re-thrown as // IllegalStateException. try { report.setFileStoragePropsPresent(true); File storageDir = getFileStorage(db); report.setFileStorage(storageDir); report.setFolderExists(storageDir.exists()); report.setFolderHasContent(folderHasContent(storageDir)); // check if the path has already been validated if (validRp.getValue().equals("true")) { report.setVerified(true); } else { report.setVerified(false); } } catch (Exception e) { throw new IllegalStateException(e); } } } /** * Get the path of the file storage, regardless whether it is valid or not. * Throws exception if there is no storage location set. * * @throws Exception */ public File getFileStorage(Database db) throws Exception { return getFileStorage(false, db); } /** * Get the path of the file storage. Uses the application name to make it * (more?) unique. Throws exception if there is no storage location set. * * @param mustBeValid * @return * @throws Exception */ public File getFileStorage(boolean mustBeValid, Database db) throws Exception { File storage = getFileStorageRoot(mustBeValid, db); if (storage == null) { throw new Exception("No retrievable or valid file storage location present."); } return new File(storage.getAbsolutePath() + File.separator + new UsedMolgenisOptions().appName); } /** * Remove file storage location and validation status. * * @throws Exception */ public void deleteFileStorage(Database db) throws Exception { RuntimeProperty pathRp = getRuntimeProperty(RUNTIME_FILE_STORAGE_PATH, db); RuntimeProperty validRp = getRuntimeProperty(RUNTIME_FILE_STORAGE_VALIDATED, db); if (pathRp == null && validRp == null) { throw new Exception("Nothing to delete"); } else if (pathRp != null && validRp == null) { throw new Exception("SEVERE: Validation prop is empty, but path is not!"); } else if (pathRp == null && validRp != null) { throw new Exception("SEVERE: Path prop is empty, but validation is not!"); } db.remove(pathRp); db.remove(validRp); report = new Report(); } /** * Ask if these is a file storage location available, regardless whether it * is valid or not. * * @param mustBeValid * @return * @throws Exception */ public boolean hasFileStorage(boolean mustBeValid, Database db) throws Exception { File storage = getFileStorageRoot(mustBeValid, db); if (storage == null) { return false; } return true; } /** * Ask if these is a valid file storage location available. * * @return * @throws Exception */ public boolean hasValidFileStorage(Database db) throws Exception { return hasFileStorage(true, db); } /** * Save this path in the form of a string to the file storage path entity. * * @param filesource * @throws Exception */ public void setFileStorage(String filesource, Database db) throws Exception { report = new Report(); if (filesource == null || filesource.equals("") || filesource.equals("null")) throw new DatabaseException( "Empty path not allowed"); filesource = addSepIfneeded(filesource); RuntimeProperty rp = getRuntimeProperty(RUNTIME_FILE_STORAGE_PATH, db); if (rp == null) { RuntimeProperty rp1 = new RuntimeProperty(); rp1.setName(RUNTIME_FILE_STORAGE_PATH); filesource = filesource.replace("\\", "\\\\"); // escape slashes for // windows? FIXME // needed?? rp1.setValue(filesource); db.add(rp1); RuntimeProperty rp2 = new RuntimeProperty(); rp2.setName(RUNTIME_FILE_STORAGE_VALIDATED); rp2.setValue("false"); db.add(rp2); } else { throw new DatabaseException("Could not set file storage: Properties already present. Please delete first."); } } /** * Run validation check on the current set file storage path. * * @throws Exception */ public void validateFileStorage(Database db) throws Exception { RuntimeProperty pathRp = getRuntimeProperty(RUNTIME_FILE_STORAGE_PATH, db); RuntimeProperty validRp = getRuntimeProperty(RUNTIME_FILE_STORAGE_VALIDATED, db); if (pathRp != null && validRp == null) { throw new Exception("SEVERE: Validation prop is empty, but path is not!"); } else if (pathRp == null && validRp != null) { throw new Exception("SEVERE: Path prop is empty, but validation is not!"); } if (pathRp == null && validRp == null) { throw new Exception("Please set file storage before validating"); } File f = new File(pathRp.getValue()); if (!f.exists()) { boolean mkDirSuccess = f.mkdirs(); if (!mkDirSuccess) { throw new Exception("Could not create directory"); } } if (f.exists()) { File tmp = new File(f.getAbsolutePath() + File.separator + "tmp" + System.currentTimeMillis() + ".txt"); boolean createSuccess = tmp.createNewFile(); if (createSuccess) { FileOutputStream fos = new FileOutputStream(tmp); DataOutputStream dos = new DataOutputStream(fos); dos.writeChars("test"); dos.close(); fos.close(); boolean deleteSuccess = tmp.delete(); if (!deleteSuccess) { throw new Exception("Could not delete file"); } } else { throw new Exception("Could not write to file"); } } else { throw new Exception("Directory does not exist"); } RuntimeProperty rp = getRuntimeProperty(RUNTIME_FILE_STORAGE_VALIDATED, db); rp.setValue("true"); db.update(rp); report.setVerified(true); } /** * Internal function. Get the root of the file storage location as a file * pointer. * * @param mustBeValid * @return * @throws DatabaseException * @throws UnsupportedEncodingException */ private File getFileStorageRoot(boolean mustBeValid, Database db) throws UnsupportedEncodingException, DatabaseException { URI loc = getURIStorageRoot(mustBeValid, db); if (loc == null) { return null; } String decodedURI = URLDecoder.decode(loc.toString(), "UTF-8"); File f = new File(decodedURI.toString()); return f; } /** * Internal function. Get the root of the file storage location as a URI. * * @param mustBeValid * @return */ private URI getURIStorageRoot(boolean mustBeValid, Database db) throws UnsupportedEncodingException, DatabaseException { RuntimeProperty path = getRuntimeProperty(RUNTIME_FILE_STORAGE_PATH, db); RuntimeProperty valid = getRuntimeProperty(RUNTIME_FILE_STORAGE_VALIDATED, db); if (path == null || valid == null) { return null; } if (mustBeValid && !valid.getValue().equals("true")) { return null; } String dir = path.getValue(); if (!DetectOS.getOS().startsWith("windows")) { // if there is no starting seperator (ie. /data) if (!dir.startsWith(File.separator)) { // add one dir = File.separator + dir; } } String encodedToUrl = URLEncoder.encode(dir, "UTF-8"); URI res = URI.create(encodedToUrl); return res; } /** * Helper function * * @return * @throws DatabaseException */ private RuntimeProperty getRuntimeProperty(String propName, Database db) throws DatabaseException { QueryRule name = new QueryRule(RuntimeProperty.NAME, Operator.EQUALS, propName); List<RuntimeProperty> rpList = null; try { rpList = db.find(RuntimeProperty.class, name); } catch (Exception e) { // maybe database or table does not exist yet // or db.find comes up null return null; } if (rpList.size() == 0) { return null; } else if (rpList.size() == 1) { return rpList.get(0); } else { throw new DatabaseException("SEVERE: RuntimeProperty name '" + propName + "' not unique"); } } /** * If OS is unix like, and path does not start with a separator, add * separator in front of the path * * @param path * @return * @throws Exception */ private String addSepIfneeded(String path) throws Exception { if (!DetectOS.getOS().startsWith("windows") && !path.startsWith(File.separator)) { path = File.separator + path; } return path; } /** * Helper function * * @param path * @return */ private boolean folderHasContent(File f) { if (f.exists()) { if (f.listFiles().length == 0) { return false; } else { return true; } } return false; } public Report getReport() { return report; } }