de.innovationgate.webgate.api.jdbc.filehandling.CS41FileAttachmentHandler.java Source code

Java tutorial

Introduction

Here is the source code for de.innovationgate.webgate.api.jdbc.filehandling.CS41FileAttachmentHandler.java

Source

/*******************************************************************************
 * Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
 * 
 * This file is part of the OpenWGA server platform.
 * 
 * OpenWGA 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 3 of the License, or
 * (at your option) any later version.
 * 
 * In addition, a special exception is granted by the copyright holders
 * of OpenWGA called "OpenWGA plugin exception". You should have received
 * a copy of this exception along with OpenWGA in file COPYING.
 * If not, see <http://www.openwga.com/gpl-plugin-exception>.
 * 
 * OpenWGA 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 OpenWGA in file COPYING.
 * If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/

package de.innovationgate.webgate.api.jdbc.filehandling;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Blob;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.codec.binary.Hex;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;

import de.innovationgate.utils.TemporaryFile;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGBackendException;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGDocument.SaveAction;
import de.innovationgate.webgate.api.WGExtensionDataContainer;
import de.innovationgate.webgate.api.WGFactory;
import de.innovationgate.webgate.api.WGFileMetaData;
import de.innovationgate.webgate.api.WGIllegalArgumentException;
import de.innovationgate.webgate.api.WGIllegalStateException;
import de.innovationgate.webgate.api.WGUpdateLog;
import de.innovationgate.webgate.api.jdbc.AttachmentFilePart;
import de.innovationgate.webgate.api.jdbc.Content;
import de.innovationgate.webgate.api.jdbc.EntityContainingFiles;
import de.innovationgate.webgate.api.jdbc.ExtensionData;
import de.innovationgate.webgate.api.jdbc.LogEntry;
import de.innovationgate.webgate.api.jdbc.MainEntity;
import de.innovationgate.webgate.api.jdbc.WGDocumentImpl;

public class CS41FileAttachmentHandler implements FileAttachmentHandler {

    public static final int ATTACHMENT_FILEPART_SIZE = 1024 * 64 - 1;

    protected CS41FileHandling _handling;
    protected WGDocumentImpl _doc;
    protected EntityContainingFiles _entity;

    /**
     * map of Content- or ContainerFileMeta objects removed on this entity during this session - mapped by fileName
     * for all these metas - file data is deleted via HQL on save()
     */
    protected Map<String, CS41FileAttachmentEntity> _removedFiles = new HashMap<String, CS41FileAttachmentEntity>();

    /**
     * map of Content- or ContainerFileMetas attached on this entity - mapped by fileName
     * during this session
     */
    protected Map<String, AttachFileOperation<CS41FileAttachmentEntity>> _attachedFiles = new HashMap<String, AttachFileOperation<CS41FileAttachmentEntity>>();

    private Map<String, WGFileMetaData> _modifiedMetaData = new HashMap<String, WGFileMetaData>();

    protected CS41FileEntityDescriptor _entityDescriptor;

    protected CS41FileAttachmentHandler(CS41FileHandling handling, WGDocumentImpl doc,
            CS41FileEntityDescriptor entityDescriptor) {
        _handling = handling;
        _doc = doc;
        _entity = (EntityContainingFiles) doc.getEntity();
        _entityDescriptor = entityDescriptor;
    }

    public AttachFileOperation<CS41FileAttachmentEntity> attachFile(File file, String fileName)
            throws WGAPIException {

        // See if a file of that name is already attached
        if (_entity.getFileEntities().containsKey(fileName) && !_removedFiles.containsKey(fileName)) {
            // B0000471E
            throw new WGIllegalArgumentException("A file with the same name '" + fileName
                    + "' is already attached on this document, please remove it first.");
        }

        // Create meta entity and attach to parent entity
        Date now = new Date();

        CS41FileAttachmentEntity fileMeta = (CS41FileAttachmentEntity) _entity.getFileEntities().get(fileName);
        if (fileMeta == null) {
            fileMeta = _entityDescriptor.createNewFile(fileName, now, _entity);
            _entity.getFileEntities().put(fileName, fileMeta);
        }

        fileMeta.setSize(file.length());
        fileMeta.setLastmodified(now);
        fileMeta.setSourceFile(file);

        AttachFileOperation<CS41FileAttachmentEntity> op = new AttachFileOperation<CS41FileAttachmentEntity>(
                fileMeta);
        _attachedFiles.put(fileName, op);
        _removedFiles.remove(fileName);

        return op;

    }

    public void removeFile(String name) throws WGAPIException {

        CS41FileAttachmentEntity fileMeta = (CS41FileAttachmentEntity) _entity.getFileEntities().get(name);
        if (fileMeta != null) {
            _removedFiles.put(name, fileMeta);
            _attachedFiles.remove(name);
            _modifiedMetaData.remove(name);
        }

    }

    public void beforeSave() throws WGAPIException {

        // Remove all data from removed files
        Iterator<CS41FileAttachmentEntity> removedMetas = _removedFiles.values().iterator();
        while (removedMetas.hasNext()) {
            FileAttachmentEntity removedMeta = removedMetas.next();
            processRemovedFile(removedMeta);
        }
        _removedFiles.clear();

        // Remove all old data from updated files (attached files whose checksum is already set)
        Iterator<AttachFileOperation<CS41FileAttachmentEntity>> attachedMetas = _attachedFiles.values().iterator();
        while (attachedMetas.hasNext()) {
            CS41FileAttachmentEntity attachedMeta = attachedMetas.next().getEntity();
            if (attachedMeta.getChecksum() != null) {
                deleteFileData(_entity, attachedMeta);
            }
        }

    }

    public void processRemovedFile(FileAttachmentEntity removedMeta) throws WGAPIException {
        deleteFileData(_entity, removedMeta);
        _entity.getFileEntities().remove(((FileAttachmentEntity) removedMeta).getName());
    }

    public void afterSave(LogEntry updateLog) throws WGAPIException {

        // Save file data
        Session session = _handling.getParent().getSession();
        Iterator<AttachFileOperation<CS41FileAttachmentEntity>> metas = _attachedFiles.values().iterator();
        while (metas.hasNext()) {
            AttachFileOperation<CS41FileAttachmentEntity> attachOp = metas.next();
            attachOp.setUpdateLog(updateLog);
            saveFileData(attachOp);
        }
        _attachedFiles.clear();

        // Save file metadata (must be after file data, bc. some metadata values are influenced by it, like the update revision)
        for (WGFileMetaData meta : _modifiedMetaData.values()) {
            saveFileMetaData(meta, updateLog);
        }
        _modifiedMetaData.clear();

    }

    public void beforeRemove() throws WGAPIException {

        EntityContainingFiles filesEntity = (EntityContainingFiles) _entity;
        Iterator<String> fileNames = _doc.getFileNames().iterator();
        while (fileNames.hasNext()) {
            String filename = (String) fileNames.next();
            FileAttachmentEntity file = fetchFileAttachmentEntity(filename);
            deleteFileData(filesEntity, file);
        }

    }

    public void afterRemove() throws WGAPIException {
    }

    public void pushFiles(WGDocumentImpl docCopy) throws WGAPIException {

        try {
            Content copy = (Content) docCopy.getEntity();
            copy.setFiles(new HashMap<String, FileAttachmentEntity>());

            // detach all files from originalImpl and attach on
            // entityCopy
            Iterator<String> fileNames = _doc.getFileNames().iterator();
            while (fileNames.hasNext()) {
                String orgFileName = fileNames.next();
                pushFile(docCopy, orgFileName);

            }
        } catch (IOException e) {
            throw new WGBackendException("Exception pushing files to document copy", e);
        }

    }

    protected AttachFileOperation<?> pushFile(WGDocumentImpl docCopy, String orgFileName)
            throws IOException, WGAPIException {
        String fileName = WGUtils.strReplace(orgFileName, "/", "", true);
        final TemporaryFile tempFile = new TemporaryFile(fileName, _doc.getFileData(orgFileName),
                WGFactory.getTempDir());
        if (docCopy.getDocument() != null) {
            docCopy.getDocument().afterSave(new SaveAction() {
                @Override
                public void run(WGDocument doc) throws Exception {
                    tempFile.delete();
                }
            });
        } else {
            tempFile.deleteOnEviction(_handling.getParent().getDb().getSessionContext());
        }

        File theFile = tempFile.getFile();
        AttachFileOperation<?> op = doPushFile(docCopy, orgFileName, theFile);
        return op;
    }

    protected AttachFileOperation<?> doPushFile(WGDocumentImpl docTarget, String fileName, File theFile)
            throws WGAPIException {
        AttachFileOperation<?> op = docTarget.getFileHandler().attachFile(theFile, fileName);
        if (op.getEntity() instanceof CS41FileAttachmentEntity) {
            CS41FileAttachmentEntity cs41Entity = (CS41FileAttachmentEntity) op.getEntity();
            cs41Entity.setCreated(getFileMetaData(fileName).getCreated());
            cs41Entity.setLastmodified(getFileMetaData(fileName).getLastmodified());
        }
        return op;
    }

    public InputStream getFileData(String name) throws WGAPIException {

        if (_removedFiles.containsKey(name)) {
            return null;
        }

        CS41FileAttachmentEntity fileEntity = fetchFileAttachmentEntity(name);
        if (fileEntity != null) {
            return getFileAttachmentEntityData(fileEntity);
        } else {
            return null;
        }

    }

    protected InputStream getFileAttachmentEntityData(CS41FileAttachmentEntity fileEntity) throws WGAPIException {
        String hqlQuery = "select cfp from " + _entityDescriptor.getHQLFileDataEntity(fileEntity)
                + " as cfp where cfp.meta=:metaEntity order by cfp.partnr asc";
        Query query = _handling.getParent().getSession().createQuery(hqlQuery);
        query.setParameter("metaEntity", fileEntity);
        return _handling.createOptimizedInputStream(query);
    }

    public WGFileMetaData getFileMetaData(String name) throws WGAPIException {

        if (_removedFiles.containsKey(name)) {
            return null;
        }

        WGFileMetaData meta = _modifiedMetaData.get(name);
        if (meta != null) {
            meta.setContext(_doc.getFileMetadataContext());
            return meta;
        }

        CS41FileAttachmentEntity metaEntity = retrieveFileMetaEntity(name);
        if (metaEntity != null) {
            meta = readMetaData(metaEntity);
            return meta;
        }

        else {
            // file not found
            return null;
        }
    }

    protected WGFileMetaData readMetaData(CS41FileAttachmentEntity metaEntity) throws WGAPIException {
        return new WGFileMetaData(_doc.getFileMetadataContext(), metaEntity.getName(), metaEntity.getSize(),
                metaEntity.getCreated(), metaEntity.getLastmodified(), metaEntity.getChecksum(), null,
                _handling.loadMdExtensionData(_doc, metaEntity));
    }

    @Override
    public FileAttachmentEntity saveFileMetaData(WGFileMetaData md, LogEntry updateLog) throws WGAPIException {

        if (_removedFiles.containsKey(md.getName())) {
            throw new WGIllegalStateException("The file of name " + md.getName() + " was deleted");
        }

        CS41FileAttachmentEntity metaEntity = retrieveFileMetaEntity(md.getName());
        if (metaEntity == null) {
            throw new WGIllegalStateException("No file of name " + md.getName() + " on this document");
        }
        _handling.storeMdExtensionData(_doc, md, metaEntity);
        metaEntity.setLastmodified(new Date());
        return metaEntity;

    }

    public void markMetaDataModified(WGFileMetaData md) {
        _modifiedMetaData.put(md.getName(), md);
    }

    protected CS41FileAttachmentEntity retrieveFileMetaEntity(String filename) {

        if (_entity instanceof EntityContainingFiles) {
            return (CS41FileAttachmentEntity) ((EntityContainingFiles) _entity).getFileEntities().get(filename);
        } else {
            return null;
        }
    }

    public int getFileSize(String name) throws WGAPIException {

        CS41FileAttachmentEntity meta = (CS41FileAttachmentEntity) _entity.getFileEntities().get(name);
        if (meta != null) {
            return (int) meta.getSize();
        } else {
            return -1;
        }

    }

    public boolean hasFile(String name) throws WGAPIException {

        if (_removedFiles.containsKey(name)) {
            return false;
        }

        return _entity.getFileEntities().containsKey(name);
    }

    public boolean isFileMetaDataAvailable() throws WGAPIException {
        return true;
    }

    public void renameFile(String name, String newName) throws WGAPIException {

        if (_entity.getFileEntities().containsKey(newName)) {
            throw new WGIllegalArgumentException("A file with the same name '" + newName
                    + "' is already attached on this document, please remove it first.");
        }

        Hibernate.initialize(_entity.getFileEntities());
        CS41FileAttachmentEntity meta = (CS41FileAttachmentEntity) _entity.getFileEntities().remove(name);
        if (meta != null) {
            meta.setName(newName);
            _entity.getFileEntities().put(meta.getName(), meta);
            meta.setLastmodified(new Date());
        }

        WGFileMetaData md = _modifiedMetaData.remove(name);
        md = readMetaData(meta);
        _modifiedMetaData.put(newName, md);

    }

    protected void deleteFileData(EntityContainingFiles filesEntity, FileAttachmentEntity meta)
            throws WGAPIException {

        if (_handling.getParent().isSaveIsolationActive() == true && (meta == null || meta.getId() == null)) {
            return;
        }

        Query query = _handling.getParent().getSession().createQuery(
                "delete " + _entityDescriptor.getHQLFileDataEntity(meta) + " cfp where cfp.meta = :meta");
        query.setEntity("meta", meta);
        query.executeUpdate();

    }

    protected CS41FileAttachmentEntity fetchFileAttachmentEntity(String strFile)
            throws WGAPIException, HibernateException {

        // We cannot query if the entity is transient
        if (((MainEntity) _entity).getCreated() == null) {
            return null;
        }

        String hqlQuery = "select cfm from " + _entityDescriptor.getHQLFileMetaEntity() + " cfm where cfm."
                + _entityDescriptor.getHQLFileMetaParentProperty() + "=:entity and cfm.name=:name";
        Query query = _handling.getParent().getSession().createQuery(hqlQuery);
        query.setParameter("entity", _entity);
        query.setParameter("name", strFile);

        Iterator<?> it = query.iterate();
        if (it.hasNext()) {
            return (CS41FileAttachmentEntity) it.next();
        } else {
            return null;
        }

    }

    @Override
    public List<String> getFileNames() throws WGAPIException {
        List<String> files = new ArrayList<String>(_entity.getFileEntities().keySet());
        files.removeAll(_removedFiles.keySet());
        return files;
    }

    public void saveFileData(AttachFileOperation<CS41FileAttachmentEntity> op) throws WGAPIException {

        try {
            if (!op.isUpdateData()) {
                return;
            }

            // create digest for checksum computation
            MessageDigest digest = null;
            try {
                digest = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                // ignore digest creation
            }

            Session session = _handling.getParent().getSession();

            CS41FileAttachmentEntity fileMeta = op.getEntity();
            InputStream in = new BufferedInputStream(new FileInputStream(fileMeta.getSourceFile()));
            // store file data
            // split up file in parts with 64K end store each part                          
            int partnr = 0;
            byte[] buffer = new byte[CS41FileAttachmentHandler.ATTACHMENT_FILEPART_SIZE];
            int len = in.read(buffer);

            while (len > 0) {
                // create new file part
                AttachmentFilePart part = _entityDescriptor.createFilePart(fileMeta);
                part.setPartnr(partnr);

                Blob data = Hibernate.getLobCreator(session).createBlob(new ByteArrayInputStream(buffer, 0, len),
                        len);
                part.setData(data);
                // store file part
                session.save(part);
                session.flush();
                session.evict(part);
                // update md5 digest
                if (digest != null) {
                    digest.update(buffer, 0, len);
                }
                // read next part from inputstream
                partnr++;
                len = in.read(buffer);
            }

            // store md5 sum as meta
            if (digest != null) {
                fileMeta.setChecksum(new String(Hex.encodeHex(digest.digest())));
                if (_handling.getParent().isSaveIsolationActive()) {
                    session.update(fileMeta);
                }
            }
        } catch (Exception e) {
            throw new WGBackendException("Exception storing file data", e);
        }

    }

    @Override
    public WGExtensionDataContainer retrieveFileExtensionDataHandler(String filename) throws WGAPIException {
        return null;
    }

    @Override
    public void addBinaryExtensionData(ExtensionData extData, Object value) throws WGAPIException {
        throw new WGIllegalStateException("Binary extension data is not supported on this content store version");
    }

    @Override
    public void saveAdditionalObject(Object obj, LogEntry logEntry) throws WGAPIException {
        _handling.getParent().getSession().saveOrUpdate(obj);
    }

}