com.xpn.xwiki.doc.XWikiAttachmentContent.java Source code

Java tutorial

Introduction

Here is the source code for com.xpn.xwiki.doc.XWikiAttachmentContent.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * 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 com.xpn.xwiki.doc;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.AutoCloseInputStream;

/**
 * The content of an attachment. Objects of this class hold the actual content which will be downloaded when a user
 * downloads an attachment.
 * 
 * @version $Id: 71d80a4a293be5d46154eb1856e92bd50347b85c $
 */
public class XWikiAttachmentContent implements Cloneable {
    /** The XWikiAttachment (attachment metadata) which this attachment content is associated with. */
    private XWikiAttachment attachment;

    /** True if the content is out of sync with the content stored in the database and thus needs to be saved. */
    private boolean isContentDirty;

    /** Storage which holds the actual content. */
    private FileItem file;

    /**
     * Constructor which clones an existing XWikiAttachmentContent. Used by {@link #clone()}.
     * 
     * @param original the XWikiAttachmentContent to clone.
     * @since 2.6M1
     */
    public XWikiAttachmentContent(XWikiAttachmentContent original) {
        this.file = original.file;
        this.attachment = original.attachment;
    }

    /**
     * Constructor with associated attachment specified.
     * 
     * @param attachment the attachment which this is the content for.
     */
    public XWikiAttachmentContent(XWikiAttachment attachment) {
        this();
        this.setAttachment(attachment);
    }

    /**
     * The default Constructor. For creating content which will be associated with an attachment later.
     */
    public XWikiAttachmentContent() {
        this.newFileItem();
    }

    /**
     * Set a new FileItem for storage.
     * 
     * @since 2.6M1
     */
    private void newFileItem() {
        String tempFileLocation = System.getProperty("java.io.tmpdir");
        // TODO try to get a different temp file location.
        try {
            final DiskFileItem dfi = new DiskFileItem(null, null, false, null, 10000, new File(tempFileLocation));
            // This causes the temp file to be created.
            dfi.getOutputStream();
            // Make sure this file is marked for deletion on VM exit because DiskFileItem does not.
            dfi.getStoreLocation().deleteOnExit();
            this.file = dfi;
        } catch (IOException e) {
            throw new RuntimeException("Failed to create new attachment temporary file."
                    + " Are you sure you have permission to write to " + tempFileLocation + "?", e);
        }
    }

    /**
     * This is used so that Hibernate will associate this content with the right attachment (metadata).
     * 
     * @return the id of the attachment (metadata) which this content is associated with.
     */
    public long getId() {
        return this.attachment.getId();
    }

    /**
     * This function does nothing and exists only for Hibernate to be able to load a value which is not used.
     * 
     * @param id is ignored.
     */
    public void setId(long id) {
        // Do nothing here.
        // The id is taken from the attachment which is set in XWikiHibernateAttachmentStore#loadAttachmentContent.
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.lang.Object#clone()
     */
    @Override
    public Object clone() {
        return new XWikiAttachmentContent(this);
    }

    /**
     * @return a byte array containing the binary content of the attachment.
     * @deprecated use {@link #getContentInputStream()} instead
     */
    @Deprecated
    public byte[] getContent() {
        return this.file.get();
    }

    /**
     * Set the content from a byte array.
     * 
     * @param content a byte array containing the binary data of the attachment
     * @deprecated use {@link #setContent(java.io.InputStream, int)} instead
     */
    @Deprecated
    public void setContent(byte[] content) {
        try {
            byte[] internalContent = new byte[0];
            if (content != null) {
                internalContent = content;
            }

            this.setContent(new ByteArrayInputStream(internalContent));
        } catch (IOException e) {
            throw new RuntimeException("Failed to copy data to storage.", e);
        }
    }

    /**
     * @return which attachment (Metadata) this content belongs to.
     */
    public XWikiAttachment getAttachment() {
        return this.attachment;
    }

    /**
     * @param attachment which attachment (metadata) this content is to be associated with.
     */
    public void setAttachment(XWikiAttachment attachment) {
        this.attachment = attachment;
    }

    /**
     * Is the content "dirty" meaning out of sync with the database.
     * 
     * @return true if the content is out of sync with the database and in need of saving.
     */
    public boolean isContentDirty() {
        return this.isContentDirty;
    }

    /**
     * Set the content as "dirty" meaning out of sync with the database.
     * 
     * @param contentDirty if true then the content is regarded as out of sync with the database and in need of saving,
     *            otherwise it's considered saved.
     */
    public void setContentDirty(boolean contentDirty) {
        this.isContentDirty = contentDirty;
    }

    /**
     * @return an InputStream to read the binary content of this attachment.
     * @since 2.3M2
     */
    public InputStream getContentInputStream() {
        try {
            return new AutoCloseInputStream(this.file.getInputStream());
        } catch (IOException e) {
            throw new RuntimeException("Failed to get InputStream", e);
        }
    }

    /**
     * Set the content of the attachment from a portion of an InputStream.
     * 
     * @param is the input stream that will be read
     * @param len the number of bytes to read from the beginning of the stream
     * @throws IOException when an error occurs during streaming operation
     * @since 2.3M2
     */
    public void setContent(InputStream is, int len) throws IOException {
        // TODO Fix so this sends a EOS when the limit is reached.
        // this.setContent(new LimitedInputStream(is, ((long) len)));
        this.setContent(is);
    }

    /**
     * Set the content of the attachment from an InputStream.
     * 
     * @param is the input stream that will be read
     * @throws IOException when an error occurs during streaming operation
     * @since 2.6M1
     */
    public void setContent(InputStream is) throws IOException {
        this.newFileItem();
        IOUtils.copy(is, this.file.getOutputStream());
        this.setContentDirty(true);

        this.attachment.setFilesize(this.getSize());
    }

    /**
     * @return the true size of the content of the attachment.
     * @since 2.3M2
     */
    public int getSize() {
        return (int) this.file.getSize();
    }
}