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

Java tutorial

Introduction

Here is the source code for com.xpn.xwiki.doc.FilesystemAttachmentContent.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.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.AutoCloseInputStream;

/**
 * The content of an attachment. This implementation is based on a file on the filesystem.
 * This implementation is immutable and is only created by the
 * {@link com.xpn.xwiki.store.FilesystemAttachmentStore}.
 *
 * @version $Id: 4b6ed952468440f58dd82fb7b62a8ca3b10ecfb0 $
 * @since 3.0M2
 */
public class FilesystemAttachmentContent extends XWikiAttachmentContent {
    /**
     * The underlying storage mechanism.
     */
    private final File storageFile;

    /**
     * A lock which is locked when the attachment content is being read.
     */
    private final ReadWriteLock lock;

    /**
     * The Constructor.
     *
     * @param storage the file where the data is stored.
     * @param attachment the attachment to associate this content with.
     * @param lock this will be locked for reading when the attachment file is being read.
     */
    public FilesystemAttachmentContent(final File storage, final XWikiAttachment attachment,
            final ReadWriteLock lock) {
        // TODO This will cause a new FileItem to be created in XWikiAttachmentContent
        // but it is the only constructor available. This should be fixed in XAC.
        super(attachment);

        this.storageFile = storage;
        this.lock = lock;
    }

    @Override
    public FilesystemAttachmentContent clone() {
        return new FilesystemAttachmentContent(this.storageFile, this.getAttachment(), this.lock);
    }

    @Override
    @Deprecated
    public byte[] getContent() {
        final InputStream is = this.getContentInputStream();
        try {
            return IOUtils.toByteArray(is);
        } catch (IOException e) {
            throw new RuntimeException("Failed to load attachment content", e);
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    @Override
    public InputStream getContentInputStream() {
        /** An InputStream which locks a lock while it is being read. */
        final class LockingFileInputStream extends FileInputStream {
            /** The lock to lock while reading the file. */
            private Lock lock;

            /**
             * The Constructor.
             *
             * @param toRead the file for this stream to read.
             * @param lock the lock to lock on creation of the stream and unlock when it is closed.
             * @throws IOException if the extended FileInputStream throws one.
             */
            public LockingFileInputStream(final File toRead, final Lock lock) throws IOException {
                super(toRead);
                this.lock = lock;
                lock.lock();
            }

            /** {@inheritDoc} */
            public void close() throws IOException {
                // Make sure this only happens once.
                if (this.lock != null) {
                    super.close();
                    this.lock.unlock();
                    this.lock = null;
                }
            }
        }

        try {
            return new AutoCloseInputStream(new LockingFileInputStream(this.storageFile, this.lock.readLock()));
        } catch (IOException e) {
            throw new RuntimeException("Failed to get InputStream", e);
        }
    }

    @Override
    public void setContent(final InputStream is) throws IOException {
        // This should be immutable but XWikiAttachment calls this when an attachment is being
        // saved which already exists. Disconnect this content from the attachment since it
        // might be being used by another instance of the same attachment, then let the attachment
        // create a new XWikiAttachmentContent instance with a new file.
        this.getAttachment().setAttachment_content(null);
        this.getAttachment().setContent(is);
    }

    @Override
    public int getSize() {
        long size = this.storageFile.length();
        // The most important thing is that it doesn't roll over into the negative space.
        if (size > ((long) Integer.MAX_VALUE)) {
            return Integer.MAX_VALUE;
        }
        return (int) size;
    }

    /*
     * Despite being an immutable implementation, setContentDirty(boolean) is not overridden,
     * this is because there is the possibility that the attachment will be moved from one
     * document to another, from versioning store to main store, or from deleted attachments to
     * main store and the core uses setContentDirty and isContentDirty as a way to signal
     * that there is reason to want to save the attachment.
     */
}