001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.vfs2.provider.ram;
018
019import java.io.ByteArrayInputStream;
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.OutputStream;
023
024import org.apache.commons.lang3.ArrayUtils;
025import org.apache.commons.vfs2.FileObject;
026import org.apache.commons.vfs2.FileSystemException;
027import org.apache.commons.vfs2.FileSystemOptions;
028import org.apache.commons.vfs2.FileType;
029import org.apache.commons.vfs2.RandomAccessContent;
030import org.apache.commons.vfs2.provider.AbstractFileName;
031import org.apache.commons.vfs2.provider.AbstractFileObject;
032import org.apache.commons.vfs2.util.FileObjectUtils;
033import org.apache.commons.vfs2.util.RandomAccessMode;
034
035/**
036 * A RAM File contains a single RAM FileData instance, it provides methods to access the data by implementing FileObject
037 * interface.
038 */
039public class RamFileObject extends AbstractFileObject<RamFileSystem> {
040
041    /**
042     * RAM File Object Data.
043     */
044    private RamFileData data;
045
046    /**
047     * Constructs a new instance.
048     *
049     * @param fileName the file name.
050     * @param fileSystem the file system.
051     */
052    protected RamFileObject(final AbstractFileName fileName, final RamFileSystem fileSystem) {
053        super(fileName, fileSystem);
054        getAbstractFileSystem().attach(this);
055    }
056
057    /*
058     * (non-Javadoc)
059     *
060     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doAttach()
061     */
062    @Override
063    protected void doAttach() throws Exception {
064        getAbstractFileSystem().attach(this);
065    }
066
067    /*
068     * (non-Javadoc)
069     *
070     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doCreateFolder()
071     */
072    @Override
073    protected void doCreateFolder() throws Exception {
074        injectType(FileType.FOLDER);
075        save();
076    }
077
078    /*
079     * (non-Javadoc)
080     *
081     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doDelete()
082     */
083    @Override
084    protected void doDelete() throws Exception {
085
086        if (isContentOpen()) {
087            throw new FileSystemException(getName() + " cannot be deleted while the file is open");
088        }
089        getAbstractFileSystem().delete(this);
090    }
091
092    /*
093     * (non-Javadoc)
094     *
095     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetContentSize()
096     */
097    @Override
098    protected long doGetContentSize() throws Exception {
099        return size();
100    }
101
102    /*
103     * (non-Javadoc)
104     *
105     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetInputStream()
106     */
107    @Override
108    protected InputStream doGetInputStream(final int bufferSize) throws Exception {
109        // VFS-210: ram allows to gather an input stream even from a directory. So we need to check the type anyway.
110        if (!getType().hasContent()) {
111            throw new FileSystemException("vfs.provider/read-not-file.error", getName());
112        }
113
114        return new ByteArrayInputStream(data.getContent());
115    }
116
117    /*
118     * (non-Javadoc)
119     *
120     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetLastModifiedTime()
121     */
122    @Override
123    protected long doGetLastModifiedTime() throws Exception {
124        return data.getLastModified();
125    }
126
127    /*
128     * (non-Javadoc)
129     *
130     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetOutputStream(boolean)
131     */
132    @Override
133    protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
134        if (!bAppend) {
135            data.setContent(ArrayUtils.EMPTY_BYTE_ARRAY);
136        }
137        return new RamFileOutputStream(this);
138    }
139
140    /*
141     * (non-Javadoc)
142     *
143     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetRandomAccessContent(
144     * org.apache.commons.vfs2.util.RandomAccessMode)
145     */
146    @Override
147    protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
148        return new RamFileRandomAccessContent(this, mode);
149    }
150
151    /*
152     * (non-Javadoc)
153     *
154     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetType()
155     */
156    @Override
157    protected FileType doGetType() throws Exception {
158        return data.getType();
159    }
160
161    /*
162     * (non-Javadoc)
163     *
164     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doListChildren()
165     */
166    @Override
167    protected String[] doListChildren() throws Exception {
168        return getAbstractFileSystem().listChildren(getName());
169    }
170
171    /*
172     * (non-Javadoc)
173     *
174     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doRename(org.apache.commons.vfs2.FileObject)
175     */
176    @Override
177    protected void doRename(final FileObject newFile) throws Exception {
178        final RamFileObject newRamFileObject = (RamFileObject) FileObjectUtils.getAbstractFileObject(newFile);
179        getAbstractFileSystem().rename(this, newRamFileObject);
180    }
181
182    /*
183     * (non-Javadoc)
184     *
185     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doSetLastModifiedTime(long)
186     */
187    /** @since 2.0 */
188    @Override
189    protected boolean doSetLastModifiedTime(final long modtime) throws Exception {
190        data.setLastModified(modtime);
191        return true;
192    }
193
194    /*
195     * (non-Javadoc)
196     *
197     * @see org.apache.commons.vfs2.provider.AbstractFileObject#endOutput()
198     */
199    @Override
200    protected void endOutput() throws Exception {
201        super.endOutput();
202        save();
203    }
204
205    /**
206     * @return the data.
207     */
208    RamFileData getData() {
209        return data;
210    }
211
212    /*
213     * (non-Javadoc)
214     *
215     * @see org.apache.commons.vfs2.provider.AbstractFileObject#injectType(org.apache.commons.vfs2.FileType)
216     */
217    @Override
218    protected void injectType(final FileType fileType) {
219        data.setType(fileType);
220        super.injectType(fileType);
221    }
222
223    /**
224     * @param newSize The new buffer size.
225     * @throws IOException if the new size exceeds the limit
226     */
227    synchronized void resize(final long newSize) throws IOException {
228        final RamFileSystem afs = getAbstractFileSystem();
229        final FileSystemOptions afsOptions = afs.getFileSystemOptions();
230        if (afsOptions != null) {
231            final long maxSize = RamFileSystemConfigBuilder.getInstance().getLongMaxSize(afsOptions);
232            if (afs.size() + newSize - size() > maxSize) {
233                throw new IOException("FileSystem capacity (" + maxSize + ") exceeded.");
234            }
235        }
236        data.resize(newSize);
237    }
238
239    private void save() throws FileSystemException {
240        getAbstractFileSystem().save(this);
241    }
242
243    /**
244     * @param data The data to set.
245     */
246    void setData(final RamFileData data) {
247        this.data = data;
248    }
249
250    /**
251     * @return the size of the {@link RamFileData}.
252     */
253    int size() {
254        return data == null ? 0 : data.size();
255    }
256
257}