Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
Merge pull request #64 from zanata/isolate-file-storage
Browse files Browse the repository at this point in the history
Isolate blob file storage
  • Loading branch information
Patrick Huang committed Jul 23, 2013
2 parents 9067f4a + f868346 commit f3c20c9
Show file tree
Hide file tree
Showing 13 changed files with 301 additions and 80 deletions.
Expand Up @@ -23,14 +23,11 @@
import static org.zanata.rest.dto.stats.TranslationStatistics.StatUnit.WORD;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.Serializable;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Blob;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -45,7 +42,6 @@
import lombok.Getter;
import lombok.Setter;

import org.hibernate.LobHelper;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
Expand All @@ -69,6 +65,8 @@
import org.zanata.dao.ProjectIterationDAO;
import org.zanata.exception.VirusDetectedException;
import org.zanata.exception.ZanataServiceException;
import org.zanata.file.FilePersistService;
import org.zanata.file.GlobalDocumentId;
import org.zanata.model.HAccount;
import org.zanata.model.HAccountRole;
import org.zanata.model.HDocument;
Expand All @@ -83,7 +81,6 @@
import org.zanata.rest.dto.stats.ContainerTranslationStatistics;
import org.zanata.rest.dto.stats.TranslationStatistics;
import org.zanata.rest.dto.stats.TranslationStatistics.StatUnit;
import org.zanata.rest.service.FileService;
import org.zanata.rest.service.StatisticsResource;
import org.zanata.rest.service.VirusScanner;
import org.zanata.security.SecurityFunctions;
Expand Down Expand Up @@ -127,6 +124,9 @@ public class ProjectIterationFilesAction implements Serializable
@In
private LocaleDAO localeDAO;

@In("blobPersistService")
private FilePersistService filePersistService;

@In
private ProjectIterationDAO projectIterationDAO;

Expand Down Expand Up @@ -407,31 +407,19 @@ else if (docId.contains("/"))
rawDocument.setAdapterParameters(params.get());
}

FileInputStream tempFileStream = null;
try
{
tempFileStream = new FileInputStream(tempFile);
try
{
String name = projectSlug+":"+iterationSlug+":"+docId;
virusScanner.scan(tempFile, name);
Blob fileContents = documentDAO.getLobHelper().createBlob(tempFileStream, (int) tempFile.length());
rawDocument.setContent(fileContents);
documentDAO.addRawDocument(document, rawDocument);
documentDAO.flush();
}
catch (VirusDetectedException e)
{
log.warn("File failed virus scan: {}", e.getMessage());
FacesMessages.instance().add(Severity.ERROR, "uploaded file did not pass virus scan");
}

String name = projectSlug+":"+iterationSlug+":"+docId;
virusScanner.scan(tempFile, name);
}
catch (FileNotFoundException e)
catch (VirusDetectedException e)
{
log.error("Failed to open stream from temp source file", e);
FacesMessages.instance().add(Severity.ERROR, "Error saving uploaded document {0} on server, download in original format may fail.", documentFileUpload.getFileName());
log.warn("File failed virus scan: {}", e.getMessage());
FacesMessages.instance().add(Severity.ERROR, "uploaded file did not pass virus scan");
}
filePersistService.persistRawDocumentContentFromFile(rawDocument, tempFile);
documentDAO.addRawDocument(document, rawDocument);
documentDAO.flush();
}

translationFileServiceImpl.removeTempFile(tempFile);
Expand Down Expand Up @@ -521,10 +509,10 @@ public boolean isPoDocument(String docId)
return translationFileServiceImpl.isPoDocument(projectSlug, iterationSlug, docId);
}

// line not found
public boolean hasOriginal(String docPath, String docName)
{
return translationFileServiceImpl.hasPersistedDocument(projectSlug, iterationSlug, docPath, docName);
GlobalDocumentId id = new GlobalDocumentId(projectSlug, iterationSlug, docPath + docName);
return filePersistService.hasPersistedDocument(id);
}

public String extensionOf(String docPath, String docName)
Expand Down
107 changes: 107 additions & 0 deletions zanata-war/src/main/java/org/zanata/file/BlobPersistService.java
@@ -0,0 +1,107 @@
/*
* Copyright 2013, Red Hat, Inc. and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.zanata.file;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.SQLException;

import javax.ws.rs.core.Response.Status;

import lombok.extern.slf4j.Slf4j;

import org.hibernate.LobHelper;
import org.hibernate.Session;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.zanata.dao.DocumentDAO;
import org.zanata.exception.ChunkUploadException;
import org.zanata.model.HDocument;
import org.zanata.model.HDocumentUploadPart;
import org.zanata.model.HRawDocument;

@Name("blobPersistService")
@Scope(ScopeType.STATELESS)
@AutoCreate
@Slf4j
public class BlobPersistService implements FilePersistService, UploadPartPersistService
{

@In
private Session session;
@In
private DocumentDAO documentDAO;

public HDocumentUploadPart newUploadPartFromStream(InputStream partContentStream, int contentLength)
{
HDocumentUploadPart newPart = new HDocumentUploadPart();
Blob partContent = session.getLobHelper().createBlob(partContentStream, contentLength);
newPart.setContent(partContent);
return newPart;
}

public void persistRawDocumentContentFromFile(HRawDocument rawDocument, File rawFile)
{
FileInputStream tempFileStream;
try
{
tempFileStream = new FileInputStream(rawFile);
}
catch (FileNotFoundException e)
{
// TODO damason: throw more appropriate exception and handle in caller
log.error("Failed to open stream from temp source file", e);
throw new ChunkUploadException(Status.INTERNAL_SERVER_ERROR,
"Error saving uploaded document on server, download in original format may fail.\n",
e);
}
LobHelper lobHelper = documentDAO.getLobHelper();
Blob fileContents = lobHelper.createBlob(tempFileStream, (int) rawFile.length());
rawDocument.setContent(fileContents);
}

@Override
public InputStream getRawDocumentContentAsStream(HRawDocument document)
{
try
{
return document.getContent().getBinaryStream();
}
catch (SQLException e)
{
throw new RawDocumentContentAccessException(e);
}
}

@Override
public boolean hasPersistedDocument(GlobalDocumentId id)
{
HDocument doc = documentDAO.getByProjectIterationAndDocId(id.getProjectSlug(),
id.getVersionSlug(), id.getDocId());
return doc.getRawDocument() != null;
}
}
Expand Up @@ -28,7 +28,6 @@
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.Vector;

Expand Down Expand Up @@ -77,6 +76,8 @@ public class DocumentUploadUtil
private ProjectIterationDAO projectIterationDAO;
@In
private TranslationFileService translationFileServiceImpl;
@In("blobPersistService")
private UploadPartPersistService uploadPartPersistService;

// TODO damason: move all validation checks to separate class
public void failIfUploadNotValid(GlobalDocumentId id, DocumentFileUploadForm uploadForm)
Expand Down Expand Up @@ -238,9 +239,9 @@ private HDocumentUpload createMultipartUpload(GlobalDocumentId id, DocumentFileU

private void saveUploadPart(DocumentFileUploadForm uploadForm, HDocumentUpload upload)
{
Blob partContent = session.getLobHelper().createBlob(uploadForm.getFileStream(), uploadForm.getSize().intValue());
HDocumentUploadPart newPart = new HDocumentUploadPart();
newPart.setContent(partContent);
InputStream contentStream = uploadForm.getFileStream();
int contentLength = uploadForm.getSize().intValue();
HDocumentUploadPart newPart = uploadPartPersistService.newUploadPartFromStream(contentStream, contentLength);
upload.getParts().add(newPart);
session.saveOrUpdate(upload);
session.flush();
Expand Down
42 changes: 42 additions & 0 deletions zanata-war/src/main/java/org/zanata/file/FilePersistService.java
@@ -0,0 +1,42 @@
/*
* Copyright 2013, Red Hat, Inc. and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.zanata.file;

import java.io.File;
import java.io.InputStream;

import org.zanata.model.HRawDocument;

public interface FilePersistService
{

public void persistRawDocumentContentFromFile(HRawDocument rawDocument, File rawFile);

// TODO damason: parsing code only needs a file URI for this. Change to return
// uri when files are persisted to server.
// Other implementations may need a way to specify that they are finished with the
// document resource and cleanup is possible, in case temp files were generated.
public InputStream getRawDocumentContentAsStream(HRawDocument document)
throws RawDocumentContentAccessException;

boolean hasPersistedDocument(GlobalDocumentId id);

}
@@ -0,0 +1,40 @@
/*
* Copyright 2013, Red Hat, Inc. and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.zanata.file;

@SuppressWarnings("serial")
public class RawDocumentContentAccessException extends RuntimeException
{
public RawDocumentContentAccessException(String message)
{
super(message);
}

public RawDocumentContentAccessException(Throwable cause)
{
super(cause);
}

public RawDocumentContentAccessException(String message, Throwable cause)
{
super(message, cause);
}
}

0 comments on commit f3c20c9

Please sign in to comment.