Commit f7c160f7 authored by Sara Bertocco's avatar Sara Bertocco
Browse files

New working version after Victoria trip

parent bda69128
Loading
Loading
Loading
Loading
+112 −467
Original line number Diff line number Diff line
/**_____________________________________________________________________________
/**
 * _____________________________________________________________________________
 *
 *                                 OATS - INAF
 *  Osservatorio Astronomico di Tireste - Istituto Nazionale di Astrofisica
@@ -21,69 +22,36 @@
 * 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;

import it.inaf.oats.vospacebackend.implementation.VOSpaceBackend;
import it.inaf.oats.vospacebackend.implementation.VOSpaceBackImplFactory;
import it.inaf.oats.vospacebackend.exceptions.ExceptionMessage;
import it.inaf.oats.vospacebackend.exceptions.VOSpaceBackendException;
import it.inaf.oats.vospacebackend.utils.ConfigReader;

import java.io.File;
import java.io.FileOutputStream;
import it.inaf.oats.vospacebackend.utils.ResourceManager;
import ca.nrc.cadc.uws.ExecutionPhase;
import ca.nrc.cadc.uws.Job;
import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.Iterator;

import javax.servlet.ServletContext;
import java.net.MalformedURLException;

import org.apache.log4j.Logger;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils; 
import org.apache.commons.codec.binary.Hex;

import org.restlet.resource.Get;
import org.restlet.resource.Post;
import org.restlet.resource.Put;
import org.restlet.resource.Delete;
import org.restlet.Request;
import org.restlet.resource.ServerResource;
import org.restlet.representation.Variant;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.representation.FileRepresentation;

import org.restlet.ext.fileupload.RestletFileUpload;

import org.restlet.data.MediaType;
import org.restlet.data.Status;

import java.security.InvalidKeyException;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;  
import java.io.OutputStream;
import java.util.UUID;
import javax.xml.bind.DatatypeConverter;

import ca.nrc.cadc.util.RsaSignatureGenerator;
import ca.nrc.cadc.util.RsaSignatureVerifier;
import java.security.MessageDigest;
import org.apache.http.HttpStatus;

/**
 *
 * @author bertocco
 */
public class VOSpaceBackendResource extends ServerResource {
public class VOSpaceBackendResource extends ServerResource implements org.apache.http.HttpStatus {

    protected Logger log = Logger.getLogger(VOSpaceBackendResource.class);

@@ -91,459 +59,136 @@ public class VOSpaceBackendResource extends ServerResource {
    public Representation doPut(Representation entity, Variant variant) throws Exception {

        Representation result = null;
        int opResult = 0;

        log.info("Entering in PUT operation");

        String vosuri = null;
        String jobID = null;
        String vosuri;
        String encodedParameters;
        InputStream is;
        if (entity != null) {
            log.info("Received good entity");
            try {
                log.debug("Trying to read attributes");
                Request request = getRequest();
                encodedParameters = (String)getRequest().getAttributes().get("parameters");
                log.debug("Received encoded parameters : " + encodedParameters);
                vosuri = manageParametersDecoding(encodedParameters);
                log.debug("Received parameters decoded = " + vosuri);
            } catch (Exception e) {
                log.debug("Exception reading string parameters");
                log.debug(e);

                vosuri = readParameters();

                jobID = (String) getRequest().getAttributes().get("jobid");
                log.debug("Received jobid = " + jobID);

            } catch (MalformedURLException e) {

                setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
                return this.printMessage("Malformed URL received. Unable to read parameters from URL");

            }

            try {

                is = entity.getStream();
                log.debug("Input stream get");
                result = readAndSaveFile(vosuri, is);
                opResult = ResourceManager.readAndSaveFile(vosuri, is);

            } catch (Exception e) {
                result = this.printMessage("File NOT Uploaded! Something went wrong.");    
            }    

        }
                log.debug("Exception in readAndSaveFile");
                return this.printMessage("File NOT Uploaded! Something went wrong.");

        return result;
            }

/*        
    @Post
    public Representation doPost(Representation entity) throws Exception {
        
        Representation result = null;
        log.info("Entering in POST operation");
    
        if (entity != null) {
        
            if (MediaType.MULTIPART_FORM_DATA.equals(entity.getMediaType(), true)) {
                log.info("Correctly Using MULTIPART_FORM_DATA");
            
                try {
                    result = this.uploadFile(entity);
                } catch (Exception e) {                
                    result = this.printMessage(e.getMessage());               
        }

        if (opResult == HttpStatus.SC_OK) {

            log.debug("File upload successful!");
            log.debug("Going to set execution phase as completed ...");
            ResourceManager.setJobPhase(jobID, ExecutionPhase.EXECUTING, ExecutionPhase.COMPLETED);
            log.debug("Checkp 1");
            ExecutionPhase expected = ExecutionPhase.COMPLETED;
            ExecutionPhase current = ResourceManager.getJobPhase(jobID);
            log.debug("Current phase = " + current.toString());
            if (current == null) {
                log.debug("Unable to correctly get job phase");
                return this.printMessage("File SUCCESSFULLY Uploaded, but Unable to correctly set/get job phase");
            } else { 
                // POST request with unexpected type.
                setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
                result = this.printMessage("POST request with unexpected type.");
                if (current.toString().equals(expected.toString())) {
                    log.debug("Job phase at the backend upload end: " + current.toString());
                    setStatus(Status.SUCCESS_OK);
                    return this.printMessage("File SUCCESSFULLY Uploaded!");
                } else {
                    log.debug("Job phase at the backend upload end: " + current.toString());
                    return this.printMessage("File SUCCESSFULLY Uploaded, but internal operation trace not completed");
                }
            }
            
            
        } else {
            // POST request with no entity.
            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
            result = this.printMessage("POST request with no entity.");
            log.debug("Internal server error. Operation result code: " + String.valueOf(opResult));
            setStatus(Status.SERVER_ERROR_INTERNAL);
            return this.printMessage("File NOT Uploaded! Something went wrong.");
        }
        return result;

    }
*/

    @Get
    public Representation doGet() {

        Representation result = null;
        String vosuri;

        log.info("Entering in GET operation");
        String fileName = (String)getRequestAttributes().get("fileToManage");
        log.debug("File to download is: " + fileName);
        try {
            result = this.downloadFile(fileName);
        } catch (Exception e) {  
            //setStatus(Status.CLIENT_ERROR_NOT_FOUND);
            result = this.printMessage("GET request: failed to download file.");  
        }  

            vosuri = readParameters();

        return result;
    }
    
    
    @Delete
    public Representation doDelete(){
        
        Representation result;
        
        log.info("Entering in DELETE operation");
        
        String fileName = (String)getRequestAttributes().get("fileToManage");
        log.debug("File to delete is: " + fileName);
        try {       
            result = this.deleteFile(fileName);
        } catch (Exception e) {         
            result = this.printMessage("DELETE request: failed to delete file " + fileName);  
        }   
        return result;                
        
    }
/*    
    private Representation uploadFile(Representation entity) throws Exception {
                
        Representation result = null;
         
        
        // 1/ Create a factory for disk-based file items
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(1000240);
        log.info("Factory created");
        // 2/ Create a new file upload handler based on the Restlet
        // FileUpload extension that will parse Restlet requests and
        // generates FileItems.
        RestletFileUpload upload = new RestletFileUpload(factory);
        log.info("RestletFileUpload created");
        
        List<FileItem> items;
        
        boolean fileFound = false; 
        String origFileName = null;           
        FileItem fileItemToStore = null;
        Map<String, String> parameters = new HashMap<String, String>();
               
        try {
            // 3/ Request is parsed by the handler which generates a list of FileItems
            items = upload.parseRequest(getRequest());
            
            for (final Iterator<FileItem> it = items.iterator(); it.hasNext(); ) {
                FileItem fi = it.next();
                String fileName = fi.getName();
                if (fileName == null) {
                    parameters.put(fi.getFieldName(), new String(fi.get(), "UTF-8"));
                } else {
                    fileItemToStore = fi;
                    origFileName = fileName;
                    fileFound = true;
                }
            }
        } catch (Exception e) {
            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
            result = this.printMessage("Unable to correctly parse request");
            return result;
        }
        
        if (!fileFound) {
            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
            result = this.printMessage("Unable to find a file to download");
            return result;
        }
        } catch (MalformedURLException e) {

        String unique_file_id_str;
        String security_token;
        
        if (parameters.isEmpty()) {
            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
            result = this.printMessage("Unable to find parameters in client request");
            return result;
        }
            return this.printMessage("Malformed URL received. Unable to read parameters from URL");

        if (origFileName == null) {
            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
            result = this.printMessage("Unable to find original file name in client request");
            return result;
        }
        
        if (parameters.get("unique_file_id_string") == null) {
            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
            result = this.printMessage("No parameter unique_file_id_string found in request");
            return result;
        }
        unique_file_id_str = parameters.get("unique_file_id_string");
        
    
        if (parameters.get("security_token") == null) {
            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
            result = this.printMessage("No parameter security_token found in request");
            return result;
        }
        security_token = parameters.get("security_token");
        
        log.debug("unique_file_id_string = " + unique_file_id_str);
        
        log.debug("security_token = " + security_token);
        
        log.debug("fileName = " + origFileName);
        
        
        result = readAndSaveFile(vosuri, fileItemToStore);
        
        return result;
        
    }
    */
    
    private Representation readAndSaveFile(String vosuri, FileItem fi)
                           throws IOException, VOSpaceBackendException {
                
        Representation result;
        log.debug("Entering in readAndSaveFile");
        
        InputStream is = fi.getInputStream();
        log.debug("Input stream get");
        
        return readAndSaveFile(vosuri, is);
        
    }
    
    
    private Representation readAndSaveFile(String vosuri, InputStream is)
                           throws IOException, VOSpaceBackendException {
        
        Representation result;
        String md5sum = null;
        
        // Generate the unique file identifier (storageFileID)
        String unique_file_id_str = UUID.randomUUID().toString();
        log.debug("Unique file identifyer " + unique_file_id_str);
        
        // Get temporary document root from configuration file
        String tmpStorageRoot = new String();
        try {
            ConfigReader myConf = new ConfigReader("VOSpace.properties");
            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"), "VOSpace.properties"));
                      throw new VOSpaceBackendException(MessageFormat.format(
                             exMsg.getMessage("PROPERTY_NOT_FOUND"), "fs.posix.tmp.storage.root", "VOSpace.properties"));
        }
        // Create the temporary directory, if needed
        File path = new File(tmpStorageRoot);
        if (!path.exists()) {
            boolean status = path.mkdirs();
        }
        // Seve the temporary file in temporary location with the new unique name
        File savedUploadedFile = new File(path + File.separator + unique_file_id_str);
        FileOutputStream outStream = new FileOutputStream(savedUploadedFile);
        try {
            md5sum = readWriteAndGetChecksum(is, outStream);
        } catch (Exception e) {
            result = this.printMessage("File NOT Uploaded! Something went wrong." + e.getMessage());    
        }

        try {
            if (this.storeUploadedFile(vosuri, unique_file_id_str, md5sum)) {  
                setStatus(Status.SUCCESS_OK); 
                result = this.printMessage("File successfully uploaded");
            } else {                  
                result = this.printMessage("File NOT Uploaded! Something went wrong.");
            }
            result = ResourceManager.downloadFile(vosuri);
        } catch (Exception e) {
            result = this.printMessage("File NOT Uploaded! Something went wrong.");    
        }
            
        return result;            
    }
    
    
    private boolean storeUploadedFile(String vosuri, String tmp_file_name, 
                                      String md5_sum) throws Exception {
        boolean stored = false;
        log.debug("Entering in storeUploadedFile");
        VOSpaceBackImplFactory myVOSpaceFactory = new VOSpaceBackImplFactory(); 
        log.debug("myVOSpaceFactory created");
        VOSpaceBackend myVOSpace = myVOSpaceFactory.getVOSpaceBackImpl();
        log.debug("myVOSpace get");
        stored = myVOSpace.createFile(vosuri, tmp_file_name, md5_sum);
        log.debug("File stored: " + stored);
        return stored;
                
    }
    
    private Representation downloadFile(String fileName) throws Exception {
        
        Representation result;
        
        log.debug("Entering in downloadFile");
        VOSpaceBackImplFactory myVOSpaceFactory = new VOSpaceBackImplFactory(); 
        log.debug("myVOSpaceFactory created");
        VOSpaceBackend myVOSpace = myVOSpaceFactory.getVOSpaceBackImpl();
        log.debug("myVOSpace get");
        File fileToDownload = myVOSpace.returnFile(fileName);
        if (fileToDownload != null ) {  
            log.debug("File found, fileToDownload is not null");
            FileRepresentation fr = new FileRepresentation(fileToDownload.getAbsolutePath(),
                                                             MediaType.APPLICATION_OCTET_STREAM);
            result = fr; 
            setStatus(Status.SUCCESS_OK); 
 
        } else {  
            log.debug("File NOT found, fileToDownload is null");                     
            result = this.printMessage("Unable to download file. Something went wrong!"); 
            setStatus(Status.SERVER_ERROR_INTERNAL);
            result = this.printMessage("GET request: failed to download file.");
        }

        return result;
    }
    
    private Representation deleteFile(String fileName) throws Exception {
         
        Representation result = null;
                   
        log.debug("Entering in deleteFile");
        VOSpaceBackImplFactory myVOSpaceFactory = new VOSpaceBackImplFactory(); 
        log.debug("myVOSpaceFactory created");
        VOSpaceBackend myVOSpace = myVOSpaceFactory.getVOSpaceBackImpl();
        log.debug("myVOSpace delete");
        try {       
            if(myVOSpace.deleteFile(fileName)) {
                log.debug("DELETE request: file " + fileName +  " removed.");
                result = this.printMessage("DELETE request: file " + fileName +  " removed.");                 
        if (result != null) {
            setStatus(Status.SUCCESS_OK);
            return result;
        } else {
                log.debug("DELETE request: failed to remove file " + fileName);
                result = this.printMessage("DELETE request: failed to remove file " + fileName); 
                setStatus(Status.SERVER_ERROR_INTERNAL);
            }
        } catch (Exception e) { 
            log.debug("DELETE request: failed to remove file " + fileName);
            result = this.printMessage("DELETE request: failed to remove file " + fileName); 
                setStatus(Status.SERVER_ERROR_INTERNAL);  
            setStatus(Status.CLIENT_ERROR_NOT_FOUND);
            return this.printMessage("GET request: failed to download file.");
        }

        return result;
    }

    
    

    private String manageParametersDecoding(String toBeVerified) {
    private String readParameters() throws MalformedURLException {

        String vosuri;
        int count = 0;
        
        log.debug("manageParametersEncoding BEGIN");
        String urlStr = new String(DatatypeConverter.parseBase64Binary(toBeVerified));
        log.debug("urlStr = " + urlStr);
        vosuri = urlStr.substring(0, urlStr.indexOf("|"));
        log.debug("vosuri = " + vosuri);
        String remaining = urlStr.substring(urlStr.indexOf("|")+1, urlStr.length());
        log.debug("Remaining after get vosuri = " + remaining);
        String signature = remaining.substring(remaining.indexOf("|")+1, remaining.length());
        log.debug("signature = \n" + signature);
        
        // Validation
        log.debug("I am going to create RsaSignatureVerifier");
        RsaSignatureVerifier su = new RsaSignatureVerifier();
        log.debug("Created");
        
        boolean valid = false;
        try {
        valid = su.verify(new ByteArrayInputStream(vosuri.getBytes()),
                    DatatypeConverter.parseBase64Binary(signature));
        } catch (IOException ioe) {
            log.debug("IOException");
            log.debug(ioe.getMessage());
        } catch (InvalidKeyException ike) {
            log.debug("InvalidKeyException");           
            log.debug(ike.getMessage());       
            log.debug("Trying to read attributes");
            Request request = getRequest();
            String encodedParameters = (String) getRequest().getAttributes().get("parameters");
            log.debug("Received encoded parameters : " + encodedParameters);
            vosuri = ResourceManager.manageParametersDecoding(encodedParameters);
            log.debug("Received parameters decoded = " + vosuri);
        } catch (Exception e) {
            log.debug("Exception reading string parameters");
            log.debug(e);
            throw new MalformedURLException("Exception reading string parameters from URL");
        }

        return vosuri;

    }

    public String readWriteAndGetChecksum(InputStream istream, FileOutputStream ostream) throws Exception {
        
        log.debug("Entering in readWriteAndGetChecksum(InputStream, FileOutputStream)");
        
        int DEFAULT_BUFFER_SIZE = 1024 * 4;
        String checksum = null;
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        int bytesRead;
        
        MessageDigest msgDigest = MessageDigest.getInstance("MD5");
        
        // do an initial read to ensure there are bytes in the stream
        try {
            bytesRead = istream.read(buffer, 0, DEFAULT_BUFFER_SIZE);
            if (bytesRead <= 0) {
                // do not allow the creation of zero-length files
                log.debug("Cannot write a zero-length file.");
                throw new Exception("Cannot write a zero-length file.");
            } else
                log.debug("First bytes read OK");
        } catch (IOException ex) {
            String errorMsg = "Upstream exception while reading from "
                + istream.getClass().getName() + ": "
                + ex.getMessage();
            log.debug("IOException in the first byte reading of the incoming file");
            log.debug(errorMsg);
            //throw new TransferAbortedException(errorMsg);
            throw new Exception(errorMsg);
        }

        // Loop reading and writing data.
        msgDigest.update(buffer, 0, bytesRead);
        log.debug("First msgDigest.update OK");
        try {

            while (bytesRead >= 0) {
                ostream.write(buffer, 0, bytesRead);
                try {
                    bytesRead = istream.read(buffer, 0, DEFAULT_BUFFER_SIZE);
                    if(bytesRead > 0) {
                        msgDigest.update(buffer, 0, bytesRead);
                    }
                } catch (IOException ex) {
                    String errorMsg = "Upstream exception while reading from "
                        + istream.getClass().getName() + ": "
                        + ex.getMessage();
                    //throw new TransferAbortedException(errorMsg);                    
                    log.debug(errorMsg);                   
                    log.debug("A Exception in reading/writing file" + ex.getMessage());
                    throw new Exception(errorMsg);
                } 
            } 
            ostream.flush();
            ostream.close();
                
        } catch (IOException ex) {
            log.debug("B Exception in reading/writing file" + ex.getMessage());
        }
        
        //Get the hash's bytes
        byte[] bytes = null;
        try {
            bytes = msgDigest.digest();
        } catch (Exception ex) {
           log.debug("Exception doing msgDigest.digest()");
        }
        String md5_checksum = new String(Hex.encodeHex(bytes));
        log.debug("************************ MD5 checksum calculated: " + md5_checksum);
        
        return md5_checksum;
        
    }
    
    
    private Representation printMessage(String error) {
    public static Representation printMessage(String error) {

        StringBuilder sb = new StringBuilder("");
        sb.append(error);
        sb.append("\n");
        return new StringRepresentation(sb.toString(), MediaType.TEXT_PLAIN);
    }

}