diff --git a/src/main/java/it/inaf/oats/vospacebackend/implementation/VOSpaceBackendPosix.java b/src/main/java/it/inaf/oats/vospacebackend/implementation/VOSpaceBackendPosix.java new file mode 100644 index 0000000000000000000000000000000000000000..a13eabb391cb89e81af04c83d45a6cbff2ae1b58 --- /dev/null +++ b/src/main/java/it/inaf/oats/vospacebackend/implementation/VOSpaceBackendPosix.java @@ -0,0 +1,373 @@ +/**_____________________________________________________________________________ + * + * OATS - INAF + * Osservatorio Astronomico di Tireste - Istituto Nazionale di Astrofisica + * Astronomical Observatory of Trieste - National Institute for Astrophysics + * ____________________________________________________________________________ + * + * Copyright (C) 20016 Istituto Nazionale di Astrofisica + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * _____________________________________________________________________________ + **/ + +package it.inaf.oats.vospacebackend.implementation; + +import ca.nrc.cadc.util.FileMetadata; +import ca.nrc.cadc.vos.DataNode; +import it.inaf.oats.vospacebackend.exceptions.ExceptionMessage; +import it.inaf.oats.vospacebackend.exceptions.VOSpaceBackendException; +import it.inaf.oats.vospacebackend.utils.ConfigReader; + +import ca.nrc.cadc.vos.Node; +import ca.nrc.cadc.vos.server.NodeID; +import ca.nrc.cadc.vos.VOS; +import ca.nrc.cadc.vos.VOSURI; + +import java.sql.SQLException; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.MessageFormat; +import java.util.UUID; + +import org.apache.log4j.Logger; +import org.apache.commons.io.FileUtils; + +/** + * + * @/author bertocco + */ +public class VOSpaceBackendPosix implements VOSpaceBackend { + + private static String documentRoot; + private static String tmpStorageRoot; + private static final String CONFIG_FILE_NAME = "VOSpace.properties"; + private static final Logger log = Logger.getLogger(VOSpaceBackendImplFactory.class); + + public VOSpaceBackendPosix() throws VOSpaceBackendException { + + try { + ConfigReader myConf = new ConfigReader(CONFIG_FILE_NAME); + + this.documentRoot = myConf.getProperty("fs.posix.document.root"); + this.tmpStorageRoot = myConf.getProperty("fs.posix.tmp.storage.root"); + } catch (Exception e) { + ExceptionMessage exMsg = new ExceptionMessage(); + log.debug(MessageFormat.format(exMsg.getMessage("UNABLE_TO_READ_PROPERTIES"), CONFIG_FILE_NAME)); + throw new VOSpaceBackendException( + MessageFormat.format(exMsg.getMessage("UNABLE_TO_READ_PROPERTIES"), CONFIG_FILE_NAME)); + + } + log.debug("VOSpace Backend Document Root = " + this.documentRoot); + log.debug("VOSpace Backend Temporary Document Root = " + this.tmpStorageRoot); + + } + + + public boolean createFile(String vosuri, String tmp_file, String md5_sum) + throws VOSpaceBackendException, SQLException, IOException { + + log.debug("Entering in vospacebackendposix.createfile"); + VOSpaceBackendMetadata metadata = new VOSpaceBackendMetadata(); + + log.debug("md5_checksum received in backendposix.createFile : " + md5_sum); + String relativePath = this.createPathFromString(md5_sum); + + // Gets metadata on frontend + log.debug("SBE: Checkpint 1 -> before new DatabaseNodePersistenceExt()"); + DatabaseNodePersistenceImpl dbNodePers = new DatabaseNodePersistenceImpl(); + log.debug("SBE: Checkpint 2 -> after new DatabaseNodePersistenceExt() and before myNode = dbNodePers.get(new VOSURI(vosuri));"); + log.debug("SBE: Checkpint 2A -> vosuri = #" + vosuri + "#"); + Node myNode = null; + try { + myNode = dbNodePers.get(new VOSURI(vosuri)); + log.debug("SBE: Checkpint 3 -> after dbNodePers.get(new VOSURI(vosuri)); vosuri = #" + vosuri + "#"); + } catch (Exception e) { + log.debug("Exception getting node from persistence."); + log.debug("Exception message : " + e.getMessage()); + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + String exceptionAsString = sw.toString(); + log.debug(exceptionAsString); + } + NodeID nodeID = (NodeID)myNode.appData; + log.debug("SBE: Checkpint 4 -> nodeID = " + nodeID.toString()); + Long myNodeID = nodeID.getID(); + log.debug("SBE: Checkpint 5 -> nodeID.getID() = " + myNodeID); + + if (!(myNode instanceof DataNode)) { + log.debug("Node instance NOT found"); + throw new VOSpaceBackendException("Node instance NOT found"); + } + log.debug("SBE: Checkpint 6 "); + DataNode myDataNode = (DataNode)myNode; + + // Needs syncronisation BEGIN + boolean result = false; + synchronized (this) { + try { + + dbNodePers.setBusyState(myDataNode, VOS.NodeBusyState.notBusy, VOS.NodeBusyState.busyWithWrite); + log.debug("SBE: Checkpint 7 -> after dbNodePers.setBusyState"); + } catch (Exception ex) { + log.debug("Exception in dbNodePers.setBusyState"); + throw new VOSpaceBackendException("TransientException in dbNodePers.setBusyState"); + } + log.debug("SBE: Checkpint 8 -> tmp_file = " + tmp_file + " --- myNodeID.toString() = " + myNodeID.toString()); + FileRecord putReqData = metadata.setRequest(tmp_file, myNodeID.toString()); + log.debug("SBE: Checkpint 9 -> after metadata.setPutRequest(tmp_file, myNodeID.toString())"); + boolean myresult = putReqData.getIfOperationSuccessful(); + log.debug("SBE: Checkpint 9A -> myresult = " + myresult); + if(!myresult) { + log.debug("Fails updating table NodeStoredFileAndNode"); + throw new VOSpaceBackendException("Node instance NOT found"); + } + log.debug("SBE: Checkpint 10 -> operation successful"); + log.debug("SBE: Checkpint 10 -> tmp_file = " +tmp_file + " --- md5_sum = " + md5_sum + " --- relativePath = " + relativePath); + + + // sets backend metadata + FileRecord fileMetadata = metadata.setFile(tmp_file, md5_sum, relativePath); + log.debug("SBE: Checkpint 11 -> after metadata.setFile"); + + // Writes the file on the file system + result = fileMetadata.getIfOperationSuccessful(); + if (result) { + log.debug("File metadata successfully saved. I'm going to store the file content"); + this.fileFromTmpToFinalStorageArea(tmp_file, relativePath, "MOVE"); + } else + log.debug("File backend metadata NOT saved. Need to abort the operation"); + // Set front-end metadata and not busy + FileMetadata nodeMetadata = new FileMetadata(); + nodeMetadata.setMd5Sum(md5_sum); + log.debug("SBE: Checkpint 12 -> After nodeMetadata.setMd5Sum(md5_sum)"); + + // INSERT A CORRECT FileSize !!!!!!!!!!!!!!!!!!!! + nodeMetadata.setContentLength(345678L); + //nodeMetadata.setContentLength("file_size"); + try { + log.debug("SBE: Checkpint 13 -> myDataNode = " + myDataNode + " --- nodeMetadata = " + nodeMetadata); + dbNodePers.setFileMetadata(myDataNode, nodeMetadata, false); + log.debug("SBE: Checkpint 13 -> After dbNodePers.setFileMetadata(myDataNode, nodeMetadata, false)"); + } catch (Exception ex) { + log.debug("Exception doing databasePersistence.setFileMetadat."); + throw new VOSpaceBackendException("Node instance NOT found"); + } + } + // Needs syncronisation END + + return result; + + } + + + /* Retrieve the file, copy it in the temporary location, return the File */ + public File returnFile(String vosuri) + throws VOSpaceBackendException, SQLException, IOException { + + // Check if fileName is present in metadata DB + VOSpaceBackendMetadata metadataDB = new VOSpaceBackendMetadata(); + + // Gets metadata on frontend + NodeUtils nodeUtil = new NodeUtils(); + Long myNodeID = nodeUtil.getNodeIdLongfromVosuriStr(vosuri); + + if (myNodeID == null) { + log.debug("Problem encountered reading backend node metadata"); + return null; + } + + FileRecord backendMetadata = metadataDB.getRequest(myNodeID.toString()); + if (!backendMetadata.getIfOperationSuccessful()) { + log.debug("Backend metadata not found"); + return null; + } + + String storedFileName = backendMetadata.getStoredfileName(); + + FileRecord myMetadata = metadataDB.getFile(storedFileName); + + File outFile = null; + + if (myMetadata.getIfOperationSuccessful()) { + log.debug("Metadata record found for vosuri " + vosuri); + log.debug("storedFileName = " + myMetadata.getStoredfileName()); + log.debug("relative_path = " + myMetadata.getRelativePath()); + log.debug("md5_checksum = " + myMetadata.getMD5Checksum()); + storedFileName = myMetadata.getStoredfileName(); + String relativePath = myMetadata.getRelativePath(); + String unique_file_id_str = UUID.randomUUID().toString(); + // Needs syncronisation START + this.fileFromStorageAreaToTmp(storedFileName, unique_file_id_str, relativePath, "COPY"); + outFile = new File(this.getTmpPath() + storedFileName); + return outFile; + // Needs syncronisation END + } else { + log.debug("Metadata record NOT found for file " + vosuri); + outFile = null; + return outFile; + } + + } + + + public String createPathFromString(String initialStr) throws VOSpaceBackendException{ + + log.debug("initialStr = " + initialStr); + log.debug("initialStr.substring(initialStr.length()-2, initialStr.length())" + initialStr.substring(initialStr.length()-2)); + log.debug("initialStr.length()-4, initialStr.length()-2)" + initialStr.substring(initialStr.length()-4, initialStr.length()-2)); + + String relativePath = null; + try{ + relativePath = new String(File.separator + + initialStr.substring(initialStr.length()-4, initialStr.length()-2) + + File.separator + + initialStr.substring(initialStr.length()-2, initialStr.length())); + } catch (Exception e) { + log.debug("Exception creating partial path from string " + initialStr); + throw new VOSpaceBackendException(e); + } + log.debug("relative path = " + relativePath); + + return relativePath; + + } + + public void fileFromTmpToFinalStorageArea(String tmpfile, String relPath, String operation) + throws VOSpaceBackendException { + + + File tmpFile = new File(this.getTmpPath() + tmpfile); + File finalStoredFile = new File(this.getStoragePath(relPath) + tmpfile); + + log.debug("tmpStoredFile is: " + tmpFile); + log.debug("finalStoredFile is: " + finalStoredFile); + + this.operateOnFiles(tmpFile, finalStoredFile, operation); + + } + + public void fileFromStorageAreaToTmp(String stored, String tmp, String relativePath, String operation) + throws VOSpaceBackendException { + + File storedFile = new File(this.getStoragePath(relativePath) + stored); + File tmpFile = new File(this.getTmpPath() + tmp); + + log.debug("storedFile is: " + storedFile); + log.debug("tmpFile is: " + tmpFile); + + this.operateOnFiles(storedFile, tmpFile, operation); + + } + + + public void operateOnFiles (File A, File B, String operation) throws VOSpaceBackendException { + + log.debug("File A is: " + A); + log.debug("File B is: " + B); + log.debug("Operation required is " + operation); + + switch (operation) { + case "MOVE": + this.moveFileAToFileB(A, B); + break; + case "COPY": + this.copyFileAToFileB(A, B); + break; + default: + log.debug("Error in operation required"); + throw new VOSpaceBackendException("Error in operation required"); + } + + } + + + public boolean checksBeforeCopyOrMove(File A, File B) + throws VOSpaceBackendException { + + boolean checkOK = false; + + if (!A.exists()) { + log.debug("Move operation impossible: source file" + A.getAbsolutePath() + + "does not exists."); + throw new VOSpaceBackendException("Operation impossible: source file" + + A.getAbsolutePath() + "does not exists."); + } + + String absolutePathB = B.getAbsolutePath(); + String pathB = absolutePathB.substring(0,absolutePathB.lastIndexOf(File.separator)); + File pathBFile = new File(pathB); + if (!pathBFile.exists()) { + try { + checkOK = pathBFile.mkdirs(); + } catch (Exception e) { + log.debug("Exception creating the final destination directory of file " + + B.getAbsolutePath()); + throw new VOSpaceBackendException(e); + } + } else if (pathBFile.isDirectory()){ + checkOK = true; + } else { + log.debug("File " + pathB + " already exsists, but is not a directory."); + checkOK = false; + } + + return checkOK; + + } + + public void moveFileAToFileB (File A, File B) throws VOSpaceBackendException { + + if (this.checksBeforeCopyOrMove(A, B)) { + try { + FileUtils.moveFile(A, B); + } catch (Exception e) { + log.debug("Exception moving temporary copy of uploaded file in its final destination directory"); + throw new VOSpaceBackendException(e); + } + } + + } + + public void copyFileAToFileB (File A, File B) throws VOSpaceBackendException { + + if (this.checksBeforeCopyOrMove(A, B)) { + try { + FileUtils.copyFile(A, B); + } catch (Exception e) { + log.debug("Exception moving temporary copy of uploaded file in its final destination directory"); + throw new VOSpaceBackendException(e); + } + } + + } + + private String getStoragePath(String relativePath) { + + String storagePath = this.documentRoot + relativePath + File.separator; + return storagePath; + + } + + private String getTmpPath() { + + String tmpFilePath = this.tmpStorageRoot + File.separator; + return tmpFilePath; + + } + +}