db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From krist...@apache.org
Subject svn commit: r752114 [1/2] - in /db/derby/code/trunk: java/engine/org/apache/derby/iapi/services/monitor/ java/engine/org/apache/derby/impl/io/ java/engine/org/apache/derby/impl/io/vfmem/ java/engine/org/apache/derby/impl/services/monitor/ java/testing/...
Date Tue, 10 Mar 2009 14:02:26 GMT
Author: kristwaa
Date: Tue Mar 10 14:02:18 2009
New Revision: 752114

URL: http://svn.apache.org/viewvc?rev=752114&view=rev
Log:
DERBY-646: In-memory backend storage support.
First revision of an in-memory back end for Derby. When using the back
end, all data (except derby.log by default) will be stored in-memory only.
When the JVM is shut down, all data is lost. The database is transient.
To use the in-memory back end, supply the assoicated JDBC subSubProtocol
when creating the database (currently set to 'in-memory', see DERBY-4084
for updates).

The current state of the feature;
 o undocumented
 o under development
 o tests are lacking
 o working
   (have been run with load tests, and parts of the regression suite)
 o cannot be used with the client driver with a specification compliant
   JDBC connection URL
 o each file is limited to 256, 512, 1024 or 2048 MB depending on page size
   (4, 8, 16 or 32 KB). Can by controlled with the existing page size property.

Brief code description (most classes are in impl):
* services/monitor/BaseMonitor

  iapi/services/monitor/PersistentService
  Adds the in-memory back end to the list of known storage facotories.
  Constant for indicating the in-memory storage engine.

* io/VFMemoryStorageFactory

  The in-memory storage factory. Deals mostly with high level operations
  related to files/directories and the storage factory instance itself.

* io/vfmem/BlockedByteArray.java
  io/vfmem/BlockedByteArrayInputStream
  io/vfmem/BlockedByteArrayOutputStream

  The BlockedByteArray stores the bytes of a file in memory. It grows
  block by block, and currently a block can be 4, 8, 16 or 32 KB big.
  Once set, the block size doesn't change. The array also shrinks when
  the length is explicitly set to a smaller value by Derby.  The stream
  classes provide a stream interface for reading/writing from/to the
  BlockedByteArray.

* io/vfmem/DataStore

  Represents a database, and consists of all the virtual paths in the
  database. Deals with actions suchs as creating, removing, renaming,
  and listing the children of a given path. Virtual files and
  directories are organized in a similar fashion as on disk.

* io/vfmem/DataStoreEntry

  Represents a path, which can be either a virtual file or directory.
  A file has a BlockedByteArray assoiciated with it, a directory
  doesn't. Serves as the entry point for access to the BlockedByteArray.

* io/vfmem/VirtualFile.java

  A virtual file handle, with numerous methods for file operations.
  Basically java.io.File and some additional methods.

* io/vfmem/VirtualRandomAccessFile

  Class providing random access to the BlockedByteArray. Provides both
  read and write access.

* io/vfmem/PathUtil

  Simple utility class for path operations. May be able to get rid of
  this by now.

* tools/jar/extraDBMSclasses.properties

  Added an entry for VFMemoryStorageFactory to include the required
  classes in the jar files.

* unitTests/...

  Some unit tests for the in-memory back end.

Patch file: derby-646-2b-vfmem_first_rev.diff

Added:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/VFMemoryStorageFactory.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArray.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayInputStream.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayOutputStream.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStore.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStoreEntry.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/PathUtil.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualFile.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualRandomAccessFile.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/BlockedByteArrayTest.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/PathUtilTest.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/VirtualFileTest.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/PersistentService.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/_Suite.java
    db/derby/code/trunk/tools/jar/extraDBMSclasses.properties

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/PersistentService.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/PersistentService.java?rev=752114&r1=752113&r2=752114&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/PersistentService.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/monitor/PersistentService.java Tue Mar 10 14:02:18 2009
@@ -80,6 +80,8 @@
 	public static final String HTTP = "http";
 	public static final String HTTPS = "https";
 
+    /** Service stored in memory only (not persistent), virtual file memory. */
+    public static final String INMEMORY = "in-memory";
 
 	/**
 		The typical name for the service's properties file.

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/VFMemoryStorageFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/io/VFMemoryStorageFactory.java?rev=752114&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/io/VFMemoryStorageFactory.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/io/VFMemoryStorageFactory.java Tue Mar 10 14:02:18 2009
@@ -0,0 +1,289 @@
+/*
+
+   Derby - Class org.apache.derby.impl.io.VFMemoryStorageFactory
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+
+package org.apache.derby.impl.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.derby.impl.io.vfmem.PathUtil;
+import org.apache.derby.impl.io.vfmem.DataStore;
+import org.apache.derby.impl.io.vfmem.VirtualFile;
+
+import org.apache.derby.io.StorageFactory;
+import org.apache.derby.io.StorageFile;
+import org.apache.derby.io.WritableStorageFactory;
+
+/**
+ * A storage factory for virtual files, where the contents of the files are
+ * stored in main memory.
+ */
+public class VFMemoryStorageFactory
+        implements StorageFactory, WritableStorageFactory {
+
+    /** References to the databases created / existing. */
+    //@GuardedBy("DATABASES")
+    private static final Map DATABASES = new HashMap();
+
+    /**
+     * Deletes the database if it exists.
+     *
+     * @param dbName the database name
+     * @return {@code true} if the database was deleted, {@code false} otherwise
+     */
+    public static boolean purgeDatabase(final String dbName) {
+        // TODO: Should we check if the database is booted / active?
+        synchronized (DATABASES) {
+            DataStore store = (DataStore)DATABASES.remove(dbName);
+            if (store != null) {
+                // Delete everything.
+                store.deleteAll("/");
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /** The canonical (unique) name of the database. */
+    private String canonicalName;
+    /** The data directory of the database. */
+    private StorageFile dataDirectory;
+    /** The temporary directory for the database. */
+    private StorageFile tempDir;
+    /** The data store used for the database. */
+    private DataStore dbData;
+
+    /**
+     * Creates a new, uninitialized instance of the storage factory.
+     * <p>
+     * To initialize the instance, {@code init} must be called.
+     *
+     * @see #init
+     */
+    public VFMemoryStorageFactory() {
+        // Do nothing here, see the init-method.
+    }
+
+    /**
+     * Initializes the storage factory instance by setting up a temporary
+     * directory, the database directory and checking if the database being
+     * named already exists.
+     *
+     * @param home the value of {@code system.home} for this storage factory
+     * @param databaseName the name of the database, all relative pathnames are
+     *      relative to this name
+     * @param tempDirNameIgnored ignored
+     * @param uniqueNameIgnored ignored
+     *
+     * @exception IOException on an error (unexpected).
+     */
+    public void init(String home, String databaseName,
+                     String tempDirNameIgnored, String uniqueNameIgnored)
+            throws IOException {
+        // Handle cases where a database name is specified.
+        if (databaseName != null) {
+            // TODO: Is using java.io.File the right thing to do?
+            //       Should we just set the canonical name equal to the
+            //       specified database name instead?
+            if (home != null &&
+                    !databaseName.startsWith(String.valueOf(getSeparator()))) {
+                canonicalName = new File(home, databaseName).getCanonicalPath();
+            } else {
+                canonicalName = new File(databaseName).getCanonicalPath();
+            }
+            synchronized (DATABASES) {
+                if (DATABASES.containsKey(canonicalName)) {
+                    // Fetch the existing data store.
+                    this.dbData = (DataStore)DATABASES.get(canonicalName);
+                } else {
+                    // Create a new data store.
+                    this.dbData = new DataStore(canonicalName);
+                    DATABASES.put(canonicalName, dbData);
+                }
+            }
+            // Specify the data directory and the temp directory.
+            dataDirectory = new VirtualFile(canonicalName, dbData);
+            tempDir = new VirtualFile(getSeparator() + "tmp", dbData);
+
+        // Handle cases where the database name is null, but a system home
+        // directory has been specified.
+        } else if (home != null) {
+            // Return the "system home directory" and create a temporary
+            // directory for it.
+            final String absHome = new File(home).getCanonicalPath();
+            synchronized (DATABASES) {
+                dbData = (DataStore)DATABASES.get(absHome);
+                if (dbData == null) {
+                    // Create a new data store for the specified home.
+                    dbData = new DataStore(absHome);
+                    DATABASES.put(absHome, dbData);
+                }
+            }
+            dataDirectory = new VirtualFile(absHome, dbData);
+            tempDir = new VirtualFile(getSeparator() + "tmp", dbData);
+        }
+
+        // Create the temporary directory, if one has been specified.
+        if (tempDir != null && !tempDir.exists()) {
+            tempDir.mkdirs();
+        }
+    }
+
+    public void shutdown() {
+        // For now, do nothing.
+        // TODO: Deleting stuff doesn't seem to play nice when running the
+        // regression tests, as CleanDatabaseTestSetup fails then. The cause
+        // is unknown and should be investigated.
+    }
+
+    public String getCanonicalName() {
+        return canonicalName;
+    }
+
+    /**
+     * Returns a handle to the specific storage file.
+     *
+     * @param path the path of the file or directory
+     * @return A path handle.
+     */
+    public StorageFile newStorageFile(String path) {
+        // No need to separate between temporary and non-temporary files, since
+        // all files are non-persistant and the path will determine where the
+        // files are stored.
+        if (path == null) {
+            // Return the database directory as described by StorageFactory.
+            return dataDirectory;
+        }
+        return new VirtualFile(path, dbData);
+    }
+
+    /**
+     * Returns a handle to the specified storage file.
+     *
+     * @param directoryName the name of the parent directory
+     * @param fileName the name of the file
+     * @return A path handle.
+     */
+    public StorageFile newStorageFile(String directoryName, String fileName) {
+        return new VirtualFile(PathUtil.join(directoryName, fileName), dbData);
+    }
+
+    /**
+     * Returns a handle to the specified storage file.
+     *
+     * @param directoryName the name of the parent directory
+     * @param fileName the name of the file
+     * @return A path handle.
+     */
+    public StorageFile newStorageFile(StorageFile directoryName,
+                                      String fileName) {
+        return newStorageFile(directoryName.getPath(), fileName);
+    }
+
+    /**
+     * Returns the temporary directory for this storage factory instance.
+     *
+     * @return A {@code StorageFile}-object representing the temp directory.
+     */
+    public StorageFile getTempDir() {
+        return tempDir;
+    }
+
+    /**
+     * The service is fast and supports random access.
+     *
+     * @return {@code true}
+     */
+    public boolean isFast() {
+        return true;
+    }
+
+    /**
+     * The service supports writes.
+     *
+     * @return {@code false}
+     */
+    public boolean isReadOnlyDatabase() {
+        return false;
+    }
+
+    /**
+     * The service supports random access.
+     *
+     * @return {@code true}
+     */
+    public boolean supportsRandomAccess() {
+        return true;
+    }
+
+    public int getStorageFactoryVersion() {
+        return StorageFactory.VERSION_NUMBER;
+    }
+
+    /**
+     * Creates a handle to a temporary file.
+     *
+     * @param prefix requested prefix for the file name
+     * @param suffix requested suffix for the file name, if {@code null} then
+     *      {@code .tmp} will be used
+     * @return A handle to the temporary file.
+     */
+    public StorageFile createTemporaryFile(String prefix, String suffix) {
+        String name;
+        if (suffix == null) {
+            suffix = ".tmp";
+        }
+        if (prefix == null) {
+            name = dbData.getTempFileCounter() + suffix;
+        } else {
+            name = prefix + dbData.getTempFileCounter() + suffix;
+        }
+        return newStorageFile(tempDir, name);
+    }
+
+    /**
+     * Returns the path separator used by this storage factory.
+     *
+     * @return {@code PathUtil.SEP}
+     */
+    public char getSeparator() {
+        return PathUtil.SEP;
+    }
+
+    /**
+     * The sync method is a no-op for this storage factory.
+     *
+     * @param stream ignored
+     * @param metaData ignored
+     */
+    public void sync(OutputStream stream, boolean metaData) {
+        // Does nothing, data is stored only in memory.
+        // TODO: Are there any streams that needs to be flushed?
+    }
+
+    public boolean supportsWriteSync() {
+        // TODO: What will give us the best performance here?
+        return true;
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/VFMemoryStorageFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArray.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArray.java?rev=752114&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArray.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArray.java Tue Mar 10 14:02:18 2009
@@ -0,0 +1,316 @@
+/*
+
+   Derby - Class org.apache.derby.impl.io.vfmem.BlockedByteArray
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+*/
+
+package org.apache.derby.impl.io.vfmem;
+
+/**
+ * Stores data in blocks, and supports reading/writing data from/into these
+ * blocks.
+ * <p>
+ * The blocked array is expanded and shrunk as required.
+ * <p>
+ * The current implementation has a size limit of
+ * {@code INITIAL_BLOCK_HOLDER_SIZE * blockSize}. For the default values, this
+ * gives:
+ * <ul> <li>4 KB blocks: 256 MB
+ *      <li>8 KB blocks: 512 MB
+ *      <li>16 KB blocks: 1024 MB
+ *      <li>32 KB blocks: 2048 MB
+ * </ul>
+ */
+public class BlockedByteArray {
+
+    /** Constant for 4 KB. */
+    private static final int _4K = 4*1024;
+    /** Constant for 8 KB. */
+    private static final int _8K = 8*1024;
+    /** Constant for 16 KB. */
+    private static final int _16K = 16*1024;
+    /** Constant for 32 KB. */
+    private static final int _32K = 32*1024;
+    /** The default block size. */
+    private static final int DEFAULT_BLOCKSIZE = _4K;
+
+    /** The default number of slots for holding a block of data. */
+    private static final int INITIAL_BLOCK_HOLDER_SIZE = 64*1024;
+
+    /** References to blocks of data. */
+    private byte[][] blocks;
+    /** The size of a block of data (the allocation unit). */
+    private int blockSize;
+    /** The number of allocated blocks. */
+    private int allocatedBlocks;
+    /** The number of bytes stored in the blocked byte array. */
+    private long length;
+
+    /**
+     * Creates a new blocked byte array with the default number of slots to
+     * hold byte arrays (blocks).
+     * <p>
+     * No blocks are pre-allocated.
+     *
+     * @see #INITIAL_BLOCK_HOLDER_SIZE
+     */
+    public BlockedByteArray() {
+        blocks = new byte[INITIAL_BLOCK_HOLDER_SIZE][];
+    }
+
+    /**
+     * Returns the byte at the given position.
+     *
+     * @param pos position to read from
+     * @return A single byte.
+     */
+    public synchronized int read(long pos) {
+        if (pos < length) {
+            int block = (int)(pos / blockSize);
+            int index = (int)(pos % blockSize);
+            return (blocks[block][index] & 0xFF);
+        }
+        return -1;
+    }
+
+    /**
+     * Reads the up to {@code len} bytes.
+     *
+     * @param pos the position to start reading at
+     * @param buf the destination buffer
+     * @param offset offset into the destination buffer
+     * @param len the number of bytes to read
+     * @return The number of bytes read.
+     */
+     public synchronized int read(long pos, byte[] buf, int offset, int len) {
+        // Due to the loop condition below, we have to check the length here.
+        // The check is only required because calling code expects an exception.
+        if (len < 0) {
+            throw new ArrayIndexOutOfBoundsException(len);
+        }
+        // Check for EOF.
+        if (pos >= length) {
+            return -1;
+        }
+        // Adjust the length if required.
+        len = (int)Math.min(len, length - pos);
+        int block = (int)(pos / blockSize);
+        int index = (int)(pos % blockSize);
+        int read = 0;
+        while (read < len) {
+            int toRead = Math.min(len - read, blockSize - index);
+            System.arraycopy(blocks[block], index, buf, offset + read, toRead);
+            read += toRead;
+            block++;
+            index = 0;
+        }
+        return read;
+     }
+
+    /**
+     * Returns the number of bytes allocated.
+     *
+     * @return Bytes allocated.
+     */
+    public synchronized long length() {
+        return length;
+    }
+
+    /**
+     * Changes the allocated length of the data.
+     * <p>
+     * If the new length is larger than the current length, the blocked byte
+     * array will be extended with new blocks. If the new length is smaller,
+     * existing (allocated) blocks will be removed if possible.
+     *
+     * @param newLength the new length of the allocated data in bytes
+     */
+    public synchronized void setLength(final long newLength) {
+        final long currentCapacity = allocatedBlocks * blockSize;
+        if (newLength > currentCapacity) {
+            // Allocate more blocks.
+            increaseCapacity(newLength);
+        } else if (newLength < currentCapacity) {
+            if (newLength <= 0L) {
+                // Just clear everything.
+                allocatedBlocks = 0;
+                blocks = new byte[INITIAL_BLOCK_HOLDER_SIZE][];
+            } else {
+                // Nullify the surplus data.
+                int blocksToKeep = (int)(newLength / blockSize) +1;
+                for (int i=blocksToKeep; i <= allocatedBlocks; i++) {
+                    blocks[i] = null;
+                }
+                allocatedBlocks = Math.min(allocatedBlocks, blocksToKeep);
+            }
+        }
+        length = Math.max(0L, newLength);
+    }
+
+    /**
+     * Writes the given bytes into the blocked byte array.
+     *
+     * @param pos the position to start writing at
+     * @param buf the source buffer
+     * @param offset the offset into the source buffer
+     * @param len the number of bytes to write
+     * @return The number of bytes written.
+     */
+    public synchronized int writeBytes(final long pos, final byte[] buf,
+                                       int offset, final int len) {
+        // Optimize block size if possible on first write.
+        if (blockSize == 0) {
+            checkBlockSize(len);
+        }
+        // Due to the loop condition below, we have to check the length here.
+        // The check is only required because calling code expects an exception.
+        if (len < 0) {
+            throw new ArrayIndexOutOfBoundsException(len);
+        }
+        // Increase the capacity if required.
+        if (pos + len >= allocatedBlocks * blockSize) {
+            increaseCapacity(pos + len);
+        }
+        // Calculate the block number and the index within this block.
+        int block = (int)(pos / blockSize);
+        int index = (int)(pos % blockSize);
+
+        int written = 0;
+        while (written < len) {
+            int toWrite = Math.min(len - written, blockSize - index);
+            System.arraycopy(buf, offset, blocks[block], index, toWrite);
+            written += toWrite;
+            offset += toWrite;
+            if (written < len) {
+                block++;
+                index = 0;
+            } else {
+                index += toWrite;
+            }
+        }
+
+        // Update the length if we wrote past the previous length.
+        length = Math.max(length, pos + len);
+        return written;
+    }
+
+    /**
+     * Writes the given byte into the blocked byte array.
+     *
+     * @param pos the position to write the byte at
+     * @param b the byte to write
+     * @return {@code 1}, which is the number of bytes written.
+     */
+    public synchronized int writeByte(long pos, byte b) {
+        // Optimize block size if possible on first write.
+        if (blockSize == 0) {
+            checkBlockSize(0);
+        }
+        // Increase the capacity if required.
+        if (pos >= allocatedBlocks * blockSize) {
+            increaseCapacity(pos);
+        }
+
+        // Calculate the block number and the index within this block.
+        int block = (int)(pos / blockSize);
+        int index = (int)(pos % blockSize);
+        blocks[block][index] = b;
+        // Update the length if we wrote past the previous length.
+        length = Math.max(length, pos +1);
+        return 1; // The number of bytes written, always one.
+    }
+
+    /**
+     * Returns an input stream serving the data in the blocked byte array.
+     *
+     * @return An {@code InputStream}-object.
+     */
+    synchronized BlockedByteArrayInputStream getInputStream() {
+        return new BlockedByteArrayInputStream(this, 0L);
+    }
+
+    /**
+     * Returns an output stream writing data into the blocked byte array.
+     *
+     * @param pos initial position of the output stream
+     * @return An {@code OutputStream}-object.
+     */
+    synchronized BlockedByteArrayOutputStream getOutputStream(long pos) {
+        if (pos < 0) {
+            throw new IllegalArgumentException(
+                                        "Position cannot be negative: " + pos);
+        }
+        return new BlockedByteArrayOutputStream(this, pos);
+    }
+
+    /**
+     * Releases this array.
+     */
+    synchronized void release() {
+        blocks = null;
+        length = allocatedBlocks = -1;
+    }
+
+    /**
+     * Tries to optimize the block size by setting it equal to the the page
+     * size used by the database.
+     * <p>
+     * Since we don't have a way of knowing which page size will be used, wait
+     * to set the block size until the first write request and see how many
+     * bytes are written then.
+     *
+     * @param len the requested number of bytes to be written
+     */
+    private void checkBlockSize(int len) {
+        // Optimize on the block size (if possible).
+        if (len == _4K || len == _8K || len == _16K || len == _32K) {
+            blockSize = len;
+        } else {
+            blockSize = DEFAULT_BLOCKSIZE;
+        }
+    }
+
+    /**
+     * Increases the capacity of this blocked byte array by allocating more
+     * blocks.
+     *
+     * @param lastIndex the index that must fit into the array
+     */
+    //@GuardedBy("this")
+    private void increaseCapacity(long lastIndex) {
+        // Safe-guard to avoid overwriting existing data.
+        if (lastIndex < allocatedBlocks * blockSize) {
+            return;
+        }
+        // Calculate required number of blocks, and create those lacking.
+        // We may allocate one more array than required.
+        final int blocksRequired = (int)((lastIndex) / blockSize) +1;
+        if (blocksRequired > blocks.length) {
+            // TODO: Thrown an OOME or do something else here?
+            //       If we let the array grow unbounded, the JVM would throw
+            //       the OOME when get into the situation that all the
+            //       available memory is exhausted.
+            throw new IllegalStateException("Too big: ~" +
+                    ((lastIndex) / 1024 / 1024) + " MB");
+        }
+        for (int i=allocatedBlocks; i < blocksRequired; i++) {
+            blocks[i] = new byte[blockSize];
+        }
+        allocatedBlocks = blocksRequired;
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArray.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayInputStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayInputStream.java?rev=752114&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayInputStream.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayInputStream.java Tue Mar 10 14:02:18 2009
@@ -0,0 +1,105 @@
+/*
+
+   Derby - Class org.apache.derby.impl.io.vfmem.BlockedByteArrayInputStream
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+*/
+
+package org.apache.derby.impl.io.vfmem;
+
+import java.io.InputStream;
+
+/**
+ * An input stream reading from a blocked byte array.
+ */
+class BlockedByteArrayInputStream
+        extends InputStream {
+
+    /** The underlying source. Set to {@code null} when closed. */
+    private BlockedByteArray src;
+    /** The current position of the stream. */
+    private long pos;
+
+    /**
+     * Creates a new input stream reading from a blocked byte array.
+     *
+     * @param src the source blocked byte array
+     * @param pos the initial position to start reading from
+     */
+    public BlockedByteArrayInputStream(BlockedByteArray src, long pos) {
+        if (src == null) {
+            throw new IllegalArgumentException(
+                    "BlockedByteArray cannot be null");
+        }
+        this.src = src;
+        this.pos = pos;
+    }
+
+    /**
+     * Sets the position.
+     *
+     * @param newPos the new byte position
+     */
+    void setPosition(long newPos) {
+        this.pos = newPos;
+    }
+
+    /**
+     * Returns the current position.
+     *
+     * @return The current byte position.
+     */
+    long getPosition() {
+        return this.pos;
+    }
+
+    /**
+     * Reads a single byte.
+     *
+     * @return A byte.
+     */
+    public int read() {
+        int ret = src.read(pos);
+        if (ret != -1) {
+            pos++;
+        }
+        return ret;
+    }
+
+    /**
+     * Reads up to {@code len} bytes.
+     *
+     * @param buf destination buffer
+     * @param offset offset into the destination buffer
+     * @param len number of bytes to read
+     * @return The number of bytes read.
+     */
+    public int read(byte[] buf, int offset, int len) {
+        int ret = src.read(pos, buf, offset, len);
+        if (ret != -1) {
+            pos += ret;
+        }
+        return ret;
+    }
+
+    /**
+     * Closes the stream.
+     */
+    public void close() {
+        this.src = null;
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayInputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayOutputStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayOutputStream.java?rev=752114&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayOutputStream.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayOutputStream.java Tue Mar 10 14:02:18 2009
@@ -0,0 +1,96 @@
+/*
+
+   Derby - Class org.apache.derby.impl.io.vfmem.BlockedByteArrayOutputStream
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+*/
+
+package org.apache.derby.impl.io.vfmem;
+
+import java.io.OutputStream;
+
+/**
+ * Output stream writing bytes into an underlying blocked byte array.
+ */
+public class BlockedByteArrayOutputStream
+        extends OutputStream {
+
+    /** The underlying destination. Set to {@code null} when closed. */
+    private BlockedByteArray src;
+    /** The current position of the stream. */
+    private long pos;
+
+    /**
+     * Creates a new stream writing data into the specified blocked byte array.
+     *
+     * @param src the underlying blocked byte array
+     * @param pos the initial position of stream
+     */
+    public BlockedByteArrayOutputStream(BlockedByteArray src, long pos) {
+        if (src == null) {
+            throw new IllegalArgumentException(
+                    "BlockedByteArray cannot be null");
+        }
+        this.src = src;
+        this.pos = pos;
+    }
+
+    /**
+     * Sets the position.
+     *
+     * @param newPos the new byte position
+     */
+    void setPosition(long newPos) {
+        this.pos = newPos;
+    }
+
+    /**
+     * Returns the current position.
+     *
+     * @return The current byte position.
+     */
+    long getPosition() {
+        return this.pos;
+    }
+
+    /**
+     * Writes the single byte into the underlying blocked byte array.
+     *
+     * @param b the byte to write
+     */
+    public void write(int b) {
+        pos += src.writeByte(pos, (byte)b);
+    }
+
+    /**
+     * Writes the specified bytes into the underlying blocked byte array.
+     *
+     * @param buf source byte array
+     * @param offset index of the first byte to write
+     * @param len the number of bytes to write
+     */
+    public void write(byte[] buf, int offset, int len) {
+        pos += src.writeBytes(pos, buf, offset, len);
+    }
+
+    /**
+     * Closes the stream.
+     */
+    public void close() {
+        this.src = null;
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayOutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStore.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStore.java?rev=752114&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStore.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStore.java Tue Mar 10 14:02:18 2009
@@ -0,0 +1,309 @@
+/*
+
+   Derby - Class org.apache.derby.impl.io.vfmem.DataStore
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+*/
+
+package org.apache.derby.impl.io.vfmem;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+
+import org.apache.derby.io.StorageFile;
+
+/**
+ * A virtual data store, keeping track of all the virtual files existing and
+ * offering a set of high-level operations on virtual files.
+ */
+public final class DataStore {
+
+    /** The path separator used. */
+    private static final char SEP = PathUtil.SEP;
+
+    /** Constant for the empty String array. */
+    private static final String[] EMPTY_STR_ARR = new String[0];
+
+    /** Lock object for the file map. */
+    private final Object LOCK = new Object();
+    /** Lock object for the temporary file counter. */
+    private final Object TMP_COUNTER_LOCK = new Object();
+    /**
+     * The files exsiting in the store.
+     * <p>
+     * The initial size is set to the number of initial files of a Derby
+     * database, pluss a few more.
+     */
+    private final Map files = new HashMap(80);
+
+    /** The name of the database this store serves. */
+    private final String databaseName;
+    /** Counter used to generate unique temporary file names. */
+    private long tmpFileCounter = 0;
+
+    /**
+     * Creates a new data store.
+     *
+     * @param databaseName the name of the assoicated database
+     */
+    public DataStore(String databaseName) {
+        this.databaseName = databaseName;
+        // Create the absolute root.
+        createEntry(String.valueOf(SEP), true);
+
+    }
+
+    /**
+     * Returns the database name.
+     *
+     * @return The database name.
+     */
+    public String getDatabaseName() {
+        return this.databaseName;
+    }
+
+    /**
+     * Creates a new entry in the data store.
+     * <p>
+     * This method returns {@code null} if the path already exists, if one of
+     * the parent directories doesn't exist, or if one of the parents is a
+     * file instead of a directory.
+     *
+     * @param iPath the path of the entry
+     * @param isDir tells if the new entry shall be directory or a file
+     * @return A {@code DataStoreEntry}-instance if the entry was successfully
+     *      created, {@code null} otherwise
+     */
+    public DataStoreEntry createEntry(String iPath, boolean isDir) {
+        synchronized (LOCK) {
+            if (files.containsKey(iPath)) {
+                return null;
+            }
+            // Make sure the the parent directories exists.
+            String parent = PathUtil.getParent(iPath);
+            while (parent != null) {
+                DataStoreEntry entry = (DataStoreEntry)files.get(parent);
+                if (entry == null) {
+                    return null;
+                } else if (!entry.isDirectory()) {
+                    return null;
+                }
+                parent = PathUtil.getParent(parent);
+            }
+            DataStoreEntry newEntry = new DataStoreEntry(iPath, isDir);
+            files.put(iPath, newEntry);
+            return newEntry;
+        }
+    }
+
+    /**
+     * Creates all the parents of the specified path.
+     *
+     * @return {@code true} if all parents either already existed as directories
+     *      or were created, {@code false} otherwise
+     */
+    public boolean createAllParents(String path) {
+        if (path.charAt(path.length() -1) == SEP) {
+            path = path.substring(0, path.length() -1);
+        }
+        // If there is no path separator, only one entry will be created.
+        if (path.indexOf(SEP) == -1) {
+            return true;
+        }
+        synchronized (LOCK) {
+            int index = path.indexOf(SEP, 1); // The root always exists
+
+            while (index > 0) {
+                String subPath = path.substring(0, index);
+                DataStoreEntry entry = (DataStoreEntry)files.get(subPath);
+                if (entry == null) {
+                    createEntry(subPath, true);
+                } else if (!entry.isDirectory()) {
+                    return false;
+                }
+                index = path.indexOf(SEP, index +1);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Deletes the specified entry.
+     * <p>
+     * If the specified entry is a directory, it is only deleted if it is
+     * empty. Read-only entries are deleted.
+     *
+     * @param iPath path of the entry to delete
+     * @return {@code true} if the entry was deleted, {@code false} otherwise.
+     */
+    public boolean deleteEntry(String iPath) {
+        DataStoreEntry entry;
+        synchronized (LOCK) {
+            entry = (DataStoreEntry)files.remove(iPath);
+            if (entry != null) {
+                if (entry.isDirectory()) {
+                    String[] children = listChildren(iPath);
+                    if (children == null || children.length == 0){
+                        entry.release();
+                        // Re-add the entry.
+                        files.put(iPath, entry);
+                        return false;
+                    }
+                } else {
+                    entry.release();
+                }
+            }
+        }
+        return (entry != null);
+    }
+
+    /**
+     * Returns the entry with the specified path.
+     *
+     * @param iPath path of the entry to fetch
+     * @return {@code null} if the entry doesn't exist, the
+     *      {@code DataStoreEntry}-object otherwise.
+     */
+    public DataStoreEntry getEntry(String iPath) {
+        synchronized (LOCK) {
+            return (DataStoreEntry)files.get(iPath);
+        }
+    }
+
+    /**
+     * Deletes the specified entry and all its children.
+     *
+     * @param iPath the root entry
+     * @return {@code true} if the entry and all its children were deleted,
+     *      {@code false} if the root doesn't exist.
+     */
+    public boolean deleteAll(String iPath) {
+        synchronized (LOCK) {
+            DataStoreEntry entry = (DataStoreEntry)files.remove(iPath);
+            if (entry == null) {
+                // Delete root doesn't exist.
+                return false;
+            } else if (entry.isDirectory()) {
+                // Delete root is a directory.
+                return _deleteAll(iPath);
+            } else {
+                // Delete root is a file.
+                entry.release();
+                return true;
+            }
+        }
+    }
+
+    /**
+     * Lists the childen of the specified path.
+     *
+     * @param iPath the directory to list the children of
+     * @return An array with the relative paths of the children.
+     */
+    public String[] listChildren(String iPath) {
+        // TODO: Disallow the empty string, or use databaseName?
+        if (iPath.equals("")) {
+            throw new IllegalArgumentException(
+                    "The empty string is not a valid path");
+        }
+        // Make sure the search path ends with the separator.
+        if (iPath.charAt(iPath.length() -1) != SEP) {
+            iPath += SEP;
+        }
+        ArrayList children = new ArrayList();
+        synchronized (LOCK) {
+            Iterator paths = files.keySet().iterator();
+            String candidate;
+            while (paths.hasNext()) {
+                candidate = (String)paths.next();
+                if (candidate.startsWith(iPath)) {
+                    children.add(candidate.substring(iPath.length()));
+                }
+            }
+        }
+        return (String[])children.toArray(EMPTY_STR_ARR);
+    }
+
+    /**
+     * Moves / renames a file.
+     *
+     * @param currentFile the current file
+     * @param newFile the new file
+     * @return {@code true} if the file was moved, {@code false} if the new
+     *      file already existed or the existing file doesn't exist.
+     */
+    public boolean move(StorageFile currentFile, StorageFile newFile) {
+        synchronized (LOCK) {
+            if (files.containsKey(newFile.getPath())) {
+                return false;
+            }
+            DataStoreEntry current = (DataStoreEntry)
+                    files.remove(currentFile.getPath());
+            if (current == null) {
+                return false;
+            }
+            files.put(newFile.getPath(), current);
+            return true;
+        }
+    }
+
+    /**
+     * Deletes every child of the root path specified.
+     * <p>
+     * Note that the root itself must be removed outside of this method.
+     *
+     * @param prefixPath the root path to start deleting from
+     * @return {@code true} if all children of the root path were deleted,
+     *      {@code false} otherwise.
+     */
+    private boolean _deleteAll(String prefixPath) {
+        ArrayList toDelete = new ArrayList();
+        Iterator paths = files.keySet().iterator();
+        // Find all the entries to delete.
+        while (paths.hasNext()) {
+            String path = (String)paths.next();
+            if (path.startsWith(prefixPath)) {
+                toDelete.add(path);
+            }
+        }
+        // Note that the root itself has already been removed before this
+        // method was called. In this case, the root has to be a directory.
+        // Iterate through all entries found and release them.
+        Iterator keys = toDelete.iterator();
+        while (keys.hasNext()) {
+            DataStoreEntry entry = (DataStoreEntry)
+                    files.remove((String)keys.next());
+            if (!entry.isDirectory()) {
+                entry.release();
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns an identifier for a temporary file.
+     *
+     * @return An integer uniquely identifying a temporary file.
+     */
+    public long getTempFileCounter() {
+        synchronized (TMP_COUNTER_LOCK) {
+            return ++tmpFileCounter;
+        }
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStoreEntry.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStoreEntry.java?rev=752114&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStoreEntry.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStoreEntry.java Tue Mar 10 14:02:18 2009
@@ -0,0 +1,173 @@
+/*
+
+   Derby - Class org.apache.derby.impl.io.vfmem.DataStoreEntry
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+*/
+
+package org.apache.derby.impl.io.vfmem;
+
+import java.io.FileNotFoundException;
+
+/**
+ * A data store entry representing either a file or a directory.
+ * <p>
+ * If the entry is a directory, it doesn't create a data object.
+ */
+public class DataStoreEntry {
+
+    /** The path of this entry. */
+    private final String path;
+    /** Tells if this entry is a directory or a regular file. */
+    private final boolean isDir;
+    /** Tells if this entry is read-only or not. */
+    private boolean isReadOnly = false;
+    /** The data of the entry. */
+    private final BlockedByteArray src;
+    /** Tells if the entry has been released or not. */
+    private volatile boolean released = false;
+
+    /**
+     * Creates a new data store entry.
+     *
+     * @param path the path of the entry
+     * @param isDir whether the entry is a directory or a regular file
+     */
+    public DataStoreEntry(String path, boolean isDir) {
+        this.path = path;
+        this.isDir = isDir;
+        if (isDir) {
+            src = null;
+        } else {
+            src = new BlockedByteArray();
+        }
+    }
+
+    /**
+     * Tells if this entry is a directory.
+     *
+     * @return {@code true} if directory, {@code false} otherwise.
+     */
+    public boolean isDirectory() {
+        checkIfReleased();
+        return isDir;
+    }
+
+    /**
+     * Returns an input stream to read from this entry.
+     *
+     * @return An {@code InputStream}-object.
+     * @throws FileNotFoundException if this entry is a directory
+     */
+    BlockedByteArrayInputStream getInputStream()
+            throws FileNotFoundException {
+        checkIfReleased();
+        if (isDir) {
+            // As according to StorageFile
+            throw new FileNotFoundException("'" + path + "' is a directory");
+        }
+        return src.getInputStream();
+    }
+
+    /**
+     * Returns an output stream to write into this entry.
+     *
+     * @param append tells whether the entry should be appended or not
+     * @return An {@code OutputStream}-object.
+     * @throws FileNotFoundException if this entry is a directory, or is
+     *      read-only
+     */
+    BlockedByteArrayOutputStream getOutputStream(boolean append)
+            throws FileNotFoundException {
+        checkIfReleased();
+        if (isDir) {
+            // As according to StorageFile
+            throw new FileNotFoundException("'" + path + "' is a directory");
+        }
+        if (isReadOnly) {
+            // As according to StorageFile
+            throw new FileNotFoundException("'" + path + "' is read-only");
+        }
+        BlockedByteArrayOutputStream out;
+        if (append) {
+            out = src.getOutputStream(src.length());
+        } else {
+            // Truncate existing data.
+            src.setLength(0L);
+            out = src.getOutputStream(0L);
+        }
+        return out;
+    }
+
+    /**
+     * Returns the length of this entry.
+     *
+     * @return The length in bytes.
+     */
+    public long length() {
+        checkIfReleased();
+        // Will fail with a NullPointerException if this entry is a directory.
+        return src.length();
+    }
+
+    /**
+     * Makes this entry read-only.
+     */
+    public void setReadOnly() {
+        checkIfReleased();
+        this.isReadOnly = true;
+    }
+
+    /**
+     * Tells if this entry is read-only.
+     *
+     * @return {@code true} is read-only, {@code false} if not.
+     */
+    public boolean isReadOnly() {
+        checkIfReleased();
+        return this.isReadOnly;
+    }
+
+    /**
+     * Relases this entry.
+     */
+    void release() {
+        released = true;
+        src.release();
+    }
+
+    /**
+     * Sets the length of this entry.
+     *
+     * @param newLength the length in number of bytes
+     */
+    public void setLength(long newLength) {
+        checkIfReleased();
+        src.setLength(newLength);
+    }
+
+    /**
+     * Checks if this entry has been released.
+     *
+     * @throws IllegalStateException if the entry has been released
+     */
+    private void checkIfReleased() {
+        if (released) {
+            throw new IllegalStateException("Entry has been released.");
+        }
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/DataStoreEntry.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/PathUtil.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/PathUtil.java?rev=752114&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/PathUtil.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/PathUtil.java Tue Mar 10 14:02:18 2009
@@ -0,0 +1,110 @@
+/*
+
+   Derby - Class org.apache.derby.impl.io.vfmem.PathUtil
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+*/
+
+package org.apache.derby.impl.io.vfmem;
+
+import java.io.File;
+
+/**
+ * Helper methods to deal with paths in the in-memory "file system".
+ * <p>
+ * These methods are similar to those in {@code java.io.File}.
+ * <p>
+ * <em>Note</em>: The system has been hardcoded to use the separator specified
+ * by {@code java.io.File}.
+ */
+public class PathUtil {
+
+    public static final char SEP = File.separatorChar;
+    public static final String SEP_STR = String.valueOf(SEP);
+
+    /** This class cannot be instantiated. */
+    private PathUtil() {}
+
+    private static void basicPathChecks(String path) {
+        if (path == null) {
+            throw new IllegalArgumentException("Path is null");
+        }
+        if (!path.equals(path.trim())) {
+            throw new IllegalArgumentException("Path has not been trimmed: '" +
+                    path + "'");
+        }
+    }
+
+    /**
+     * Returns the base name of the path.
+     *
+     * @param path the path to process
+     * @return The base name of the path.
+     */
+    public static String getBaseName(String path) {
+        basicPathChecks(path);
+        int sepIndex = path.lastIndexOf(SEP);
+        if (sepIndex != -1 && sepIndex != path.length() -1) {
+            return path.substring(sepIndex +1);
+        }
+        return path;
+    }
+
+    /**
+     * Returns the parent of the path.
+     *
+     * @param path the path to process
+     * @return The parent path, which may be the empty string ({@code ""}) if
+     *      the path is a relative path, or {@code null} if XXXX TODO
+     */
+    public static String getParent(String path) {
+        basicPathChecks(path);
+        if (path.equals(SEP_STR)) {
+            return null;
+        }
+        // Remove the last separator, if it is the last char of the path.
+        if (path.length() > 0 && path.charAt(path.length() -1) == SEP) {
+            path = path.substring(0, path.length() -1);
+        }
+        // Look for the last separator.
+        int sepIndex = path.lastIndexOf(SEP);
+        if (sepIndex == 0) {
+            return SEP_STR;
+        } else if (sepIndex > 0) {
+            return path.substring(0, sepIndex);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Joins the two paths by inserting the separator chararcter between them.
+     *
+     * @param parent parent directory
+     * @param base file/directory name
+     * @return A merged path.
+     */
+    public static String join(String parent, String base) {
+        // It is not defined what happens if the base name starts with the
+        // separator character. For now, just let it be, which will result in a
+        // path with multiple separator chars next to eachother.
+        if (parent.charAt(parent.length() -1) == SEP) {
+            return parent + base;
+        }
+        return (parent + SEP + base);
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/PathUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualFile.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualFile.java?rev=752114&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualFile.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualFile.java Tue Mar 10 14:02:18 2009
@@ -0,0 +1,389 @@
+/*
+
+   Derby - Class org.apache.derby.impl.io.vfmem.VirtualFile
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+*/
+
+package org.apache.derby.impl.io.vfmem;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.derby.io.StorageFile;
+import org.apache.derby.io.StorageRandomAccessFile;
+
+/**
+ * Represents a file in the virtual file system.
+ * <p>
+ * A virtual file is not created until one of the following methods are invoked:
+ * <ul> <li>{@code createNewFile}
+ *      <li>{@code getOutputStream}
+ *      <li>{@code getRandomAccessFile}
+ *      <li>{@code mkdir}
+ *      <li>{@code mkdirs}
+ * </ul>
+ * <p>
+ * When a method that requires access to the file data or to know if the file
+ * exists or not, the assoicated data store is consulted.
+ */
+public class VirtualFile
+        implements StorageFile {
+
+    /** The path of this virtual file. */
+    private final String path;
+    /** The data store this virtual file belongs to. */
+    private final DataStore dStore;
+
+    /**
+     * Creates a new virtual file handle.
+     *
+     * @param path the path of this virtual file
+     * @param dbData the store this handle belongs to
+     */
+    public VirtualFile(String path, DataStore dbData) {
+        this.path = path;
+        this.dStore = dbData;
+    }
+
+    /**
+     * Returns the contents of the directory denoted by this file, including
+     * any sub directories and their contents.
+     *
+     * @return A list of all files and directories, or {@code null} if this file
+     *      doesn't denote a directory or doesn't exist.
+     */
+    public String[] list() {
+        DataStoreEntry entry = getEntry();
+        if (entry == null || !entry.isDirectory()) {
+            return null;
+        }
+        return dStore.listChildren(path);
+    }
+
+    /**
+     * Tells if this file can be written to.
+     *
+     * @return {@code true} if this file exists and can be written to,
+     *      {@code false} otherwise.
+     */
+    public boolean canWrite() {
+        return (getEntry() != null && !getEntry().isReadOnly());
+    }
+
+    /**
+     * Tells if this file exists.
+     *
+     * @return {@code true} if this file exists, {@code false} otherwise.
+     */
+    public boolean exists() {
+        return (getEntry() != null);
+    }
+
+    /**
+     * Tells if this file is a directory.
+     * <p>
+     * Note that {@code false} is returned if this path doesn't exist.
+     *
+     * @return {@code true} if this file represents an existing directoy,
+     *      {@code false} otherwise.
+     */
+    public boolean isDirectory() {
+        DataStoreEntry entry = getEntry();
+        if (entry != null && entry.isDirectory()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Deletes this file, of if exists.
+     *
+     * @return {@code true} if this file exists and is successfully deleted,
+     *      {@code false} otherwise.
+     */
+    public boolean delete() {
+        return dStore.deleteEntry(path);
+    }
+
+    /**
+     * Deletes the path denoted by this file and all its contents, including
+     * sub directories.
+     *
+     * @return {@code true} if this file and all contents are successfully
+     *      deleted, {@code false} otherwise.
+     */
+    public boolean deleteAll() {
+        DataStoreEntry entry = getEntry();
+        if (entry == null) {
+            return false;
+        }
+        if (entry.isDirectory()) {
+            return dStore.deleteAll(path);
+        } else {
+            return delete();
+        }
+    }
+
+    /**
+     * Returns the path of this file.
+     *
+     * @return The path of this file.
+     */
+    public String getPath() {
+        return path;
+    }
+
+    public String getCanonicalPath() {
+        // TODO: Should we return something that is canonical here?
+        //       This would typically be to include the database directory.
+        return getPath();
+    }
+
+    public String getName() {
+        return PathUtil.getBaseName(path);
+    }
+
+    public java.net.URL getURL()
+            throws java.net.MalformedURLException {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    /**
+     * Creates the the file denoted by this virtual file object.
+     *
+     * @return {@code true} if the file was successfully created, {@code false}
+     *      otherwise
+     */
+    public boolean createNewFile() {
+        return (dStore.createEntry(path, false) != null);
+    }
+
+    /**
+     * Renames the file denoted by this handle.
+     *
+     * @param newName the new name
+     * @return {@code true} if the fail was renamed, {@code false} otherwise
+     */
+    public boolean renameTo(StorageFile newName) {
+        // TODO: How to safely handle this, with regards to paths?
+        // TODO: What to do if the path denotes a non-empty directory?
+        return dStore.move(this, newName);
+    }
+
+    /**
+     * Creates the directory denoted by this virtual file if it doesn't exist.
+     * <p>
+     * For the directory to be created, it cannot exist already (either as a
+     * file or a directory), and any parent directories must exist.
+     *
+     * @return {@code true} if the directory was created, {@code false}
+     *      otherwise.
+     */
+    public boolean mkdir() {
+        DataStoreEntry entry = getEntry();
+        if (entry == null) {
+            return (dStore.createEntry(path, true) != null);
+        }
+        return false;
+    }
+
+    /**
+     * Creates the directory and any parent directories denoted by this virtual
+     * file.
+     * <p>
+     * For the directory to be created, it cannot exist already (either as a
+     * file or a directory), and all the parent elements most denote either
+     * existing directories or non-existing paths.
+     *
+     * @return {@code true} if the directory was created, {@code false}
+     *      otherwise.
+     */
+    public boolean mkdirs() {
+        DataStoreEntry entry = getEntry();
+        if (entry == null) {
+            return (dStore.createAllParents(path) &&
+                    (dStore.createEntry(path, true) != null));
+        }
+        return false;
+    }
+
+    /**
+     * Returns the length of the file.
+     * <p>
+     * If the file doesn't exists, or is a directory, {@code 0} is returned.
+     *
+     * @return The length of the existing file, or {@code 0} if the path denotes
+     *      a directory or a non-existing file.
+     */
+    public long length() {
+        DataStoreEntry entry = getEntry();
+        if (entry != null && !entry.isDirectory()) {
+            return entry.length();
+        } else {
+            return 0L;
+        }
+    }
+
+    public StorageFile getParentDir() {
+        String parent = PathUtil.getParent(path);
+        if (parent == null) {
+            return null;
+        } else {
+            return new VirtualFile(parent, dStore);
+        }
+    }
+
+    public boolean setReadOnly() {
+        // TODO: How to handle directories? Should its children/contents be
+        //       marked read-only too?
+        DataStoreEntry entry = getEntry();
+        if (entry == null) {
+            return false;
+        } else {
+            entry.setReadOnly();
+            return true;
+        }
+    }
+
+    /**
+     * Obtains an output stream for the file denoted.
+     * <p>
+     * If the file already exists, it will be truncated.
+     *
+     * @return An {@code OutputStream}-instance.
+     * @throws FileNotFoundException if the denoted path is a directory, the
+     *      file is read-only, the file cannot be created or any other reason
+     *      why an {@code OutputStream} cannot be created for the file
+     */
+    public OutputStream getOutputStream()
+            throws FileNotFoundException {
+        return getOutputStream(false);
+    }
+
+    /**
+     * Obtains an output stream for the file denoted.
+     *
+     * @param append tells if the file should be appended or truncated
+     * @return An {@code OutputStream}-instance.
+     * @throws FileNotFoundException if the denoted path is a directory, the
+     *      file is read-only, the file cannot be created or any other reason
+     *      why an {@code OutputStream} cannot be created for the file
+     */
+    public OutputStream getOutputStream(boolean append)
+            throws FileNotFoundException {
+        DataStoreEntry entry = getEntry();
+        if (entry == null) {
+            entry = dStore.createEntry(path, false);
+            // TODO: Creation will fail if the parent directories don't exist.
+            //       Is this okay, or shall we try mkdirs too?
+            if (entry == null) {
+                throw new FileNotFoundException("Unable to create file: " +
+                        path);
+            }
+        }
+        // The method in DataStore checks if the entry is read-only or a dir.
+        return entry.getOutputStream(append);
+    }
+
+    /**
+     * Returns an input stream for the file denoted.
+     *
+     * @return An {@code InputStream} instance.
+     * @throws FileNotFoundException if the file doesn't exists or it is a
+     *      directory
+     */
+    public InputStream getInputStream()
+            throws FileNotFoundException {
+        DataStoreEntry entry = getEntry();
+        if (entry == null) {
+            throw new FileNotFoundException(path);
+        }
+        // The method in DataStore checks if the entry is a directory or not.
+        return entry.getInputStream();
+    }
+
+    public int getExclusiveFileLock() {
+        // Just return success.
+        // Since the databases created by this storeage factory can only be
+        // accessed by the JVM in which it is running, there is no need to
+        // implement a locking mechansim here.
+        return StorageFile.EXCLUSIVE_FILE_LOCK;
+    }
+
+    public void releaseExclusiveFileLock() {}
+
+    /**
+     * Creates a random access file that can be used to read and write
+     * from/into the file.
+     *
+     * @param mode file mode, one of "r", "rw", "rws" or "rwd" (lower-case
+     *      letters are required)
+     * @return A {@code StorageRandomAccessFile}-instance.
+     * @throws IllegalArgumentException if the specificed mode is invalid
+     * @throws FileNotFoundException if the file denoted is a directory,
+     */
+    public StorageRandomAccessFile getRandomAccessFile(String mode)
+            throws FileNotFoundException {
+        if (!(mode.equals("r") || mode.equals("rw")
+                || mode.equals("rws") || mode.equals("rwd"))) {
+            throw new IllegalArgumentException("Invalid mode: " + mode);
+        }
+        DataStoreEntry entry = getEntry();
+        if (entry == null) {
+            if (mode.equals("r")) {
+                throw new FileNotFoundException(
+                    "Cannot read from non-existing file: " + path +
+                    " (mode=" + mode + ")");
+            }
+            // Try to create a new empty file.
+            entry = dStore.createEntry(path, false);
+            // TODO: Creation will fail if the parent directories don't exist.
+            //       Is this okay, or shall we try mkdirs too?
+            if (entry == null) {
+                throw new FileNotFoundException("Unable to create file: " +
+                        path + " (mode=" + mode + ")");
+            }
+        }
+        // Checks for read-only and directory happens in the constructor.
+        // Separate between read and write modes only.
+        return new VirtualRandomAccessFile(entry, mode.equals("r"));
+    }
+
+    /**
+     * Returns a textual representation of this file.
+     *
+     * @return Textual representation.
+     */
+    public String toString() {
+        return "(db=" + dStore.getDatabaseName() + ")" + path + "#exists=" +
+                exists() + ", isDirectory=" + isDirectory() + ", length=" +
+                length() + ", canWrite=" + canWrite();
+    }
+
+    /**
+     * Returns the data store entry denoted by this file, if it exists.
+     *
+     * @return The assoiciated {@code DataStoreEntry} if it exists,
+     *      {@code null} if it doesn't exist.
+     */
+    private DataStoreEntry getEntry() {
+       return dStore.getEntry(path);
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualFile.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualRandomAccessFile.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualRandomAccessFile.java?rev=752114&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualRandomAccessFile.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualRandomAccessFile.java Tue Mar 10 14:02:18 2009
@@ -0,0 +1,294 @@
+/*
+
+   Derby - Class org.apache.derby.impl.io.vfmem.VirtualRandomAccessFile
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+*/
+
+package org.apache.derby.impl.io.vfmem;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import org.apache.derby.io.StorageRandomAccessFile;
+
+/**
+ * A random access file capable of reading and writing from/into a virtual file
+ * whose data is represented by a {@code BlockedByteArray}.
+ * <p>
+ * If the file is opened in read-only mode and the caller invokes one of the
+ * write methods, it will fail with a {@code NullPointerException}.
+ */
+public class VirtualRandomAccessFile
+        implements StorageRandomAccessFile {
+
+    /** The source entry. */
+    private final DataStoreEntry entry;
+    /** Current position / file pointer. */
+    private long fp;
+    /** Stream used to read from the source entry. */
+    private BlockedByteArrayInputStream bIn;
+    /** Data input stream on top of the source input stream. */
+    private DataInputStream dIs;
+    /**
+     * Stream used to write into the source entry. Will be {@code null} if the
+     * file is opened in read-only mode.
+     */
+    private BlockedByteArrayOutputStream bOut;
+    /**
+     * Data output stream on top of the source output stream. Will be
+     * {@code null} if the file is opened in read-only mode.
+     */
+    private DataOutputStream dOs;
+
+    /**
+     * Creates a new virtual random access file.
+     *
+     * @param entry the source entry
+     * @param readOnly if the file should be opened read-only or not
+     * @throws FileNotFoundException if the denoted path is a directory, or
+     *      the denoted file has been marked read-only and the file is opened
+     *      for writing
+     */
+    public VirtualRandomAccessFile(DataStoreEntry entry, boolean readOnly)
+            throws FileNotFoundException {
+        this.entry = entry;
+        bIn = entry.getInputStream();
+        bIn.setPosition(0L);
+        dIs = new DataInputStream(bIn);
+        // Only create writeable streams if the mode isn't read-only.
+        if (readOnly) {
+            bOut = null;
+            dOs = null;
+        } else {
+            bOut = entry.getOutputStream(true);
+            bOut.setPosition(0L);
+            dOs = new DataOutputStream(bOut);
+        }
+    }
+
+    public void close() throws IOException {
+        dIs.close();
+        dIs = null;
+        // If opened in read-only mode, the output streams are null.
+        if (dOs != null) {
+            dOs.close();
+            dOs = null;
+        }
+        fp = Long.MIN_VALUE;
+    }
+
+    public long getFilePointer() {
+        return fp;
+    }
+
+    public long length() {
+        return entry.length();
+    }
+
+    public void seek(long newFilePointer) throws IOException {
+        if (newFilePointer < 0) {
+            throw new IOException("Negative position: " + newFilePointer);
+        }
+        fp = newFilePointer;
+        bIn.setPosition(newFilePointer);
+        // Output streams are null if opened in read-only mode.
+        if (bOut != null) {
+            bOut.setPosition(newFilePointer);
+        }
+    }
+
+    public void setLength(long newLength) {
+        if (bOut == null) {
+            throw new NullPointerException();
+        }
+        entry.setLength(newLength);
+        // If truncation took place, check file pointer.
+        if (newLength < fp) {
+            fp = newLength;
+        }
+    }
+
+    public void sync(boolean metaData) {
+        // Do nothing, everything is already synced.
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        int ret = bIn.read(b, off, len);
+        fp = bIn.getPosition();
+        return ret;
+    }
+
+    public void readFully(byte[] b) throws IOException {
+        readFully(b, 0, b.length);
+    }
+
+    public void readFully(byte[] b, int off, int len) throws IOException {
+        dIs.readFully(b, off, len);
+        fp = bIn.getPosition();
+    }
+
+    public int skipBytes(int n) {
+        if (n <= 0) {
+            return 0;
+        }
+        long skipped = Math.min(n, entry.length() - fp);
+        fp += skipped;
+        return (int)skipped;
+    }
+
+    public boolean readBoolean() throws IOException {
+        boolean ret = dIs.readBoolean();
+        fp = bIn.getPosition();
+        return ret;
+    }
+
+    public byte readByte() throws IOException {
+        byte ret = dIs.readByte();
+        fp = bIn.getPosition();
+        return ret;
+    }
+
+    public int readUnsignedByte() throws IOException {
+        int ret = dIs.readUnsignedByte();
+        fp = bIn.getPosition();
+        return ret;
+    }
+
+    public short readShort() throws IOException {
+        short ret = dIs.readShort();
+        fp = bIn.getPosition();
+        return ret;
+    }
+
+    public int readUnsignedShort() throws IOException {
+        int ret = dIs.readUnsignedShort();
+        fp = bIn.getPosition();
+        return ret;
+    }
+
+    public char readChar() throws IOException {
+        char ret = dIs.readChar();
+        fp = bIn.getPosition();
+        return ret;
+    }
+
+    public int readInt() throws IOException {
+        int ret = dIs.readInt();
+        fp = bIn.getPosition();
+        return ret;
+    }
+
+    public long readLong() throws IOException {
+        long ret = dIs.readLong();
+        fp = bIn.getPosition();
+        return ret;
+    }
+
+    public float readFloat() throws IOException {
+        float ret = dIs.readFloat();
+        fp = bIn.getPosition();
+        return ret;
+    }
+
+    public double readDouble() throws IOException {
+        double ret = dIs.readDouble();
+        fp = bIn.getPosition();
+        return ret;
+    }
+
+    public String readLine() throws IOException {
+        throw new UnsupportedOperationException("readLine");
+    }
+
+    public String readUTF() throws IOException {
+        String utfStr = dIs.readUTF();
+        fp = bIn.getPosition();
+        return utfStr;
+    }
+
+    public void write(int b) throws IOException {
+        dOs.write(b);
+        fp = bOut.getPosition();
+    }
+
+    public void write(byte[] b) throws IOException {
+        write(b, 0, b.length);
+    }
+
+    public void write(byte[] b, int off, int len) throws IOException {
+        dOs.write(b, off, len);
+        fp = bOut.getPosition();
+    }
+
+    public void writeBoolean(boolean v) throws IOException {
+        dOs.writeBoolean(v);
+        fp = bOut.getPosition();
+    }
+
+    public void writeByte(int v) throws IOException {
+        dOs.writeByte(v);
+        fp = bOut.getPosition();
+    }
+
+    public void writeShort(int v) throws IOException {
+        dOs.writeShort(v);
+        fp = bOut.getPosition();
+    }
+
+    public void writeChar(int v) throws IOException {
+        dOs.writeChar(v);
+        fp = bOut.getPosition();
+    }
+
+    public void writeInt(int v) throws IOException {
+        dOs.writeInt(v);
+        fp = bOut.getPosition();
+    }
+
+    public void writeLong(long v) throws IOException {
+        dOs.writeLong(v);
+        fp = bOut.getPosition();
+    }
+
+    public void writeFloat(float v) throws IOException {
+        dOs.writeFloat(v);
+        fp = bOut.getPosition();
+    }
+
+    public void writeDouble(double v) throws IOException {
+        dOs.writeDouble(v);
+        fp = bOut.getPosition();
+    }
+
+    public void writeBytes(String s) throws IOException {
+        dOs.writeBytes(s);
+        fp = bOut.getPosition();
+    }
+
+    public void writeChars(String s) throws IOException {
+        dOs.writeChars(s);
+        fp = bOut.getPosition();
+    }
+
+    public void writeUTF(String s) throws IOException {
+        dOs.writeUTF(s);
+        fp = bOut.getPosition();
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/vfmem/VirtualRandomAccessFile.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java?rev=752114&r1=752113&r2=752114&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java Tue Mar 10 14:02:18 2009
@@ -1705,6 +1705,8 @@
                                 "org.apache.derby.impl.io.URLStorageFactory");
         storageFactories.put( PersistentService.HTTPS,
                                 "org.apache.derby.impl.io.URLStorageFactory");
+        storageFactories.put( PersistentService.INMEMORY,
+                            "org.apache.derby.impl.io.VFMemoryStorageFactory");
     }
 
 	/**

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/BlockedByteArrayTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/BlockedByteArrayTest.java?rev=752114&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/BlockedByteArrayTest.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/BlockedByteArrayTest.java Tue Mar 10 14:02:18 2009
@@ -0,0 +1,162 @@
+/*
+
+   Derby - Class org.apache.derbyTesting.unitTests.junit.BlockedByteArrayTest
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+*/
+
+package org.apache.derbyTesting.unitTests.junit;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.derby.impl.io.vfmem.BlockedByteArray;
+import org.apache.derbyTesting.functionTests.util.streams.LoopingAlphabetStream;
+import org.apache.derbyTesting.junit.BaseTestCase;
+
+/**
+ * Basic tests of the {@code BlockedByteArrayTest}.
+ */
+public class BlockedByteArrayTest
+        extends BaseTestCase {
+
+    public BlockedByteArrayTest(String name) {
+        super(name);
+    }
+
+    public void testLengthNoInitialBlocksWriteSingleByte() {
+        BlockedByteArray src = new BlockedByteArray();
+        assertEquals(0, src.length());
+        src.writeByte(0, (byte)1);
+        assertEquals(1, src.length());
+        for (int i=0; i < 66*1024; i++) {
+            src.writeByte(1 + i, (byte)i);
+            assertEquals(i +2, src.length());
+        }
+    }
+
+    public void testLengthNoInitialBlocksWriteMultipleBytes4K() {
+        BlockedByteArray src = new BlockedByteArray();
+        byte[] buf = new byte[4*1024];
+        Arrays.fill(buf, (byte)1);
+        src.writeBytes(0, buf, 0, buf.length);
+        assertEquals(buf.length, src.length());
+        Arrays.fill(buf, (byte)2);
+        src.writeBytes(buf.length, buf, 0, buf.length);
+        assertEquals(2 * buf.length, src.length());
+        src.writeByte(69, (byte)8);
+        assertEquals(2 * buf.length, src.length());
+    }
+
+    public void testLengthNoInitialBlocksWriteMultipleBytes4KPlussAFew() {
+        BlockedByteArray src = new BlockedByteArray();
+        byte[] buf = new byte[4*1024+37];
+        Arrays.fill(buf, (byte)1);
+        src.writeBytes(0, buf, 0, buf.length);
+        assertEquals(buf.length, src.length());
+        Arrays.fill(buf, (byte)2);
+        src.writeBytes(buf.length, buf, 0, buf.length);
+        assertEquals(2 * buf.length, src.length());
+        src.writeByte(54, (byte)7);
+        assertEquals(2 * buf.length, src.length());
+    }
+
+    public void testReadArray()
+            throws IOException {
+        int size = 65*1024;
+        BlockedByteArray src = createBlockedByteArray(size);
+        byte[] buf = new byte[4*1024];
+        int read = 0;
+        while (read < size) {
+            read += src.read(read, buf, 0, buf.length);
+        }
+        src = createBlockedByteArray(size);
+        buf = new byte[2567];
+        read = 0;
+        while (read < size) {
+            read += src.read(read, buf, 0, buf.length);
+        }
+        src = createBlockedByteArray(size);
+        buf = new byte[16*1024];
+        read = 0;
+        while (read < size) {
+            read += src.read(read, buf, 0, buf.length);
+        }
+    }
+
+    public void testReadSingle()
+            throws IOException {
+        int size = 65*1024;
+        BlockedByteArray src = createBlockedByteArray(size);
+        int read = 0;
+        while (src.read(read) != -1) {
+            read++;
+        }
+    }
+
+    public void testLength()
+            throws IOException {
+        BlockedByteArray src = createBlockedByteArray(0);
+        assertEquals(0L, src.length());
+        src.writeByte(0L, (byte)1);
+        assertEquals(1L, src.length());
+        src.writeByte(0L, (byte)1);
+        assertEquals(1L, src.length());
+        src.writeByte(9L, (byte)2);
+        assertEquals(10L, src.length());
+        byte[] bytes = new byte[4096];
+        Arrays.fill(bytes, (byte)7);
+        src.writeBytes(0L, bytes, 0, bytes.length);
+        assertEquals(bytes.length, src.length());
+        src.writeBytes(bytes.length, bytes, 0, bytes.length);
+        assertEquals(2*bytes.length, src.length());
+
+        // Test setLength
+        src.setLength(55555);
+        assertEquals(55555, src.length());
+        src.setLength(44444);
+        assertEquals(44444, src.length());
+    }
+
+    public static Test suite() {
+        return new TestSuite(BlockedByteArrayTest.class);
+    }
+
+    /**
+     * Creates a blocked byte array and fills it with data.
+     *
+     * @param length requested length
+     * @return A filled blocked byte array.
+     * @throws IOException if reading from the source fails
+     */
+    private BlockedByteArray createBlockedByteArray(long length)
+            throws IOException {
+        BlockedByteArray data = new BlockedByteArray();
+        InputStream src = new LoopingAlphabetStream(length);
+        byte[] buf = new byte[4*1024];
+        long pos = 0;
+        while (pos < length) {
+            int readFromSrc = src.read(buf);
+            pos += data.writeBytes(pos, buf, 0, readFromSrc);
+        }
+        return data;
+    }
+}

Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/BlockedByteArrayTest.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message