Index: java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java
===================================================================
--- java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java	(revision 740261)
+++ java/engine/org/apache/derby/impl/services/monitor/BaseMonitor.java	(working copy)
@@ -1705,6 +1705,8 @@
                                 "org.apache.derby.impl.io.URLStorageFactory");
         storageFactories.put( PersistentService.HTTPS,
                                 "org.apache.derby.impl.io.URLStorageFactory");
+        storageFactories.put( PersistentService.VFMEM,
+                            "org.apache.derby.impl.io.VFMemoryStorageFactory");
     }
 
 	/**
Index: java/engine/org/apache/derby/impl/io/VFMemoryStorageFactory.java
===================================================================
--- java/engine/org/apache/derby/impl/io/VFMemoryStorageFactory.java	(revision 0)
+++ java/engine/org/apache/derby/impl/io/VFMemoryStorageFactory.java	(revision 0)
@@ -0,0 +1,281 @@
+/*
+
+   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 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 HashMap 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(String dbName) {
+        synchronized (databases) {
+            // TODO: Should we do something more thorough here, mostly to avoid
+            //       already held references to operate on a deleted database?
+            return (databases.remove(dbName) != null);
+        }
+    }
+
+    /** The database name. */
+    private String databaseName;
+    /** 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() {}
+
+    /**
+     * 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();
+            }
+            this.databaseName = canonicalName;
+            synchronized (databases) {
+                if (!databases.containsKey(canonicalName)) {
+                    // Create a new data store.
+                    this.dbData = new DataStore(canonicalName);
+                    databases.put(canonicalName, dbData);
+                } else {
+                    // Fetch the existing data store.
+                    this.dbData = (DataStore)databases.get(canonicalName);
+                }
+            }
+            // Specify the data directory and the temp directory.
+            dataDirectory = new VirtualFile(
+                    PathUtil.getBaseName(databaseName), dbData);
+            tempDir = new VirtualFile("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.
+            synchronized (databases) {
+                dbData = (DataStore)databases.get(home);
+                if (dbData == null) {
+                    // Create a new data store for the specified home.
+                    dbData = new DataStore(home);
+                    databases.put(home, dbData);
+                }
+            }
+            dataDirectory = new VirtualFile("systemHome", 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() {
+        // Nothing to do on shutdown.
+        // TODO: Is it possible / suitable to delete the database here?
+    }
+
+    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;
+    }
+}
Index: java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayInputStream.java
===================================================================
--- java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayInputStream.java	(revision 0)
+++ java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayInputStream.java	(revision 0)
@@ -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. */
+    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;
+    }
+}
Index: java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayOutputStream.java
===================================================================
--- java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayOutputStream.java	(revision 0)
+++ java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArrayOutputStream.java	(revision 0)
@@ -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. */
+    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;
+    }
+}
Index: java/engine/org/apache/derby/impl/io/vfmem/VirtualFile.java
===================================================================
--- java/engine/org/apache/derby/impl/io/vfmem/VirtualFile.java	(revision 0)
+++ java/engine/org/apache/derby/impl/io/vfmem/VirtualFile.java	(revision 0)
@@ -0,0 +1,382 @@
+/*
+
+   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"
+     * @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) {
+            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);
+            }
+        }
+        // Checks for read-only and directory happens in the constructor.
+        // We ignore the mode.
+        return new VirtualRandomAccessFile(entry);
+    }
+
+    /**
+     * Returns a textual representation of this file.
+     *
+     * @return Textual representation.
+     */
+    public String toString() {
+        return PathUtil.join(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);
+    }
+}
Index: java/engine/org/apache/derby/impl/io/vfmem/DataStore.java
===================================================================
--- java/engine/org/apache/derby/impl/io/vfmem/DataStore.java	(revision 0)
+++ java/engine/org/apache/derby/impl/io/vfmem/DataStore.java	(revision 0)
@@ -0,0 +1,310 @@
+/*
+
+   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.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 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 HashMap 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 file.
+                entry.release();
+                return true;
+            } else {
+                // Delete root is a directory.
+                return _deleteAll(iPath);
+            }
+        }
+    }
+
+    /**
+     * 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: Somthing funky here. Investigate and remove special case if
+        //       possible, if not then document why it is required.
+        // The empty string represents the root directory (service root), so
+        // list everything.
+        if (iPath.equals("")) {
+            return (String[])files.keySet().toArray(EMPTY_STR_ARR);
+        }
+        // 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;
+        }
+    }
+}
Index: java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArray.java
===================================================================
--- java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArray.java	(revision 0)
+++ java/engine/org/apache/derby/impl/io/vfmem/BlockedByteArray.java	(revision 0)
@@ -0,0 +1,310 @@
+/*
+
+   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>32 KB blocks: 2048 MB
+ * </ul>
+ */
+public class BlockedByteArray {
+
+    /** Constant for 4KB. */
+    private static final int _4K = 4*1024;
+    /** Constant for 8KB. */
+    private static final int _8K = 8*1024;
+    /** Constant for 16KB. */
+    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(long newLength) {
+        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(long pos, byte[] buf, int offset, int len) {
+        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) {
+        // Make sure we have at least one pre-allocated block.
+        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 == _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
+     */
+    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.
+        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;
+    }
+}
Index: java/engine/org/apache/derby/impl/io/vfmem/DataStoreEntry.java
===================================================================
--- java/engine/org/apache/derby/impl/io/vfmem/DataStoreEntry.java	(revision 0)
+++ java/engine/org/apache/derby/impl/io/vfmem/DataStoreEntry.java	(revision 0)
@@ -0,0 +1,152 @@
+/*
+
+   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;
+
+    /**
+     * 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() {
+        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 {
+        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 {
+        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 lenght of this entry.
+     *
+     * @return The length in bytes.
+     */
+    public long length() {
+        // Will fail with a NullPointerException if this entry is a directory.
+        return src.length();
+    }
+
+    /**
+     * Makes this entry read-only.
+     */
+    public void setReadOnly() {
+        this.isReadOnly = true;
+    }
+
+    /**
+     * Tells if this entry is read-only.
+     *
+     * @return {@code true} is read-only, {@code false} if not.
+     */
+    public boolean isReadOnly() {
+        return this.isReadOnly;
+    }
+
+    /**
+     * Relases this entry.
+     */
+    void release() {
+        src.release();
+    }
+
+    /**
+     * Sets the length of this entry.
+     *
+     * @param newLength the length in number of bytes
+     */
+    public void setLength(long newLength) {
+        src.setLength(newLength);
+    }
+}
Index: java/engine/org/apache/derby/impl/io/vfmem/VirtualRandomAccessFile.java
===================================================================
--- java/engine/org/apache/derby/impl/io/vfmem/VirtualRandomAccessFile.java	(revision 0)
+++ java/engine/org/apache/derby/impl/io/vfmem/VirtualRandomAccessFile.java	(revision 0)
@@ -0,0 +1,271 @@
+/*
+
+   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>
+ * The mode (i.e. read or read/write) is ignored, but instantiation will fail
+ * if the entry is marked as read-only.
+ */
+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. */
+    private BlockedByteArrayOutputStream bOut;
+    /** Data output stream on top of the source output stream. */
+    private DataOutputStream dOs;
+
+    /**
+     * Creates a new virtual random access file.
+     *
+     * @param entry the source entry
+     * @param mode
+     * @throws java.io.FileNotFoundException
+     */
+    public VirtualRandomAccessFile(DataStoreEntry entry)
+            throws FileNotFoundException {
+        this.entry = entry;
+        bIn = entry.getInputStream();
+        bIn.setPosition(0L);
+        bOut = entry.getOutputStream(true);
+        bOut.setPosition(0L);
+        dIs = new DataInputStream(bIn);
+        dOs = new DataOutputStream(bOut);
+    }
+
+    public void close() throws IOException {
+        dIs.close();
+        dIs = 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);
+        bOut.setPosition(newFilePointer);
+    }
+
+    public void setLength(long newLength) {
+        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();
+    }
+}
Index: java/engine/org/apache/derby/impl/io/vfmem/PathUtil.java
===================================================================
--- java/engine/org/apache/derby/impl/io/vfmem/PathUtil.java	(revision 0)
+++ java/engine/org/apache/derby/impl/io/vfmem/PathUtil.java	(revision 0)
@@ -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);
+    }
+}
Index: java/engine/org/apache/derby/iapi/services/monitor/PersistentService.java
===================================================================
--- java/engine/org/apache/derby/iapi/services/monitor/PersistentService.java	(revision 740261)
+++ java/engine/org/apache/derby/iapi/services/monitor/PersistentService.java	(working copy)
@@ -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 VFMEM = "mem";
 
 	/**
 		The typical name for the service's properties file.
Index: java/testing/org/apache/derbyTesting/unitTests/junit/VirtualFileTest.java
===================================================================
--- java/testing/org/apache/derbyTesting/unitTests/junit/VirtualFileTest.java	(revision 0)
+++ java/testing/org/apache/derbyTesting/unitTests/junit/VirtualFileTest.java	(revision 0)
@@ -0,0 +1,198 @@
+/*
+
+   Derby - Class org.apache.derbyTesting.unitTests.junit.VirtualFileTest
+
+   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 junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.derby.impl.io.vfmem.DataStore;
+import org.apache.derby.impl.io.vfmem.VirtualFile;
+import org.apache.derby.io.StorageFile;
+import org.apache.derbyTesting.junit.BaseTestCase;
+
+/**
+ * Basic tests of the class {@code VirtualFile}.
+ */
+public class VirtualFileTest
+        extends BaseTestCase {
+
+    private final String[] NON_EXISTING_DIRS = new String[] {
+                "this", "dir", "does", "not", "exist"};
+
+    public VirtualFileTest(String name) {
+        super(name);
+    }
+
+    public void testCreateFileInRoot() {
+        DataStore store = getStore();
+        VirtualFile vFile = new VirtualFile("service.properties", store);
+        assertFalse(new VirtualFile("service.properties", store).exists());
+        assertFalse(vFile.exists());
+    }
+
+    public void testCreateDirInRoot() {
+        DataStore store = getStore();
+        VirtualFile vFile = new VirtualFile("seg0", store);
+        assertFalse(vFile.exists());
+        vFile.mkdir();
+        assertTrue(vFile.exists());
+        assertTrue(vFile.isDirectory());
+    }
+
+    public void testCreateInvalidDir() {
+        DataStore store = getStore();
+        VirtualFile vFile = new VirtualFile(
+                PathUtilTest.join(NON_EXISTING_DIRS),
+                store);
+        assertFalse(vFile.exists());
+        VirtualFile tmp = new VirtualFile("", store);
+        assertTrue(tmp.mkdir());
+        assertFalse("Dir creation should have failed", vFile.mkdir());
+    }
+
+    public void testMkdirsValidRelative() {
+        DataStore store = getStore();
+        VirtualFile vFile = new VirtualFile(
+                PathUtilTest.join(NON_EXISTING_DIRS),
+                store);
+        assertTrue(vFile.mkdirs());
+    }
+
+    public void testMkdirsValidAbsolute() {
+        DataStore store = getStore();
+        VirtualFile vFile = new VirtualFile(
+                PathUtilTest.joinAbs(NON_EXISTING_DIRS),
+                store);
+        assertTrue(vFile.mkdirs());
+    }
+
+    public void testMkdirsInvalidAbsolute()
+            throws IOException {
+        DataStore store = getStore();
+        VirtualFile tmp = new VirtualFile(PathUtilTest.abs("directory"), store);
+        assertTrue(tmp.mkdir());
+        tmp = new VirtualFile(
+                PathUtilTest.joinAbs("directory", "afile"),
+                store);
+        assertTrue(tmp.createNewFile());
+        assertTrue(tmp.exists());
+        assertFalse(tmp.isDirectory());
+        VirtualFile vFile = new VirtualFile(
+                PathUtilTest.joinAbs("directory", "afile", "anotherdir"),
+                store);
+        assertFalse(vFile.mkdir());
+        assertFalse(vFile.mkdirs());
+    }
+
+    public void testMkdirsInvalidRelative()
+            throws IOException {
+        DataStore store = getStore();
+        VirtualFile tmp = new VirtualFile("seg0", store);
+        assertTrue(tmp.mkdir());
+        tmp = new VirtualFile(PathUtilTest.join("seg0", "datafile"), store);
+        assertTrue(tmp.createNewFile());
+        assertTrue(tmp.exists());
+        assertFalse(tmp.isDirectory());
+        VirtualFile vFile = new VirtualFile(
+                PathUtilTest.join("seg0", "datafile", "anotherdir"), store);
+        assertFalse(vFile.mkdir());
+        assertFalse(vFile.mkdirs());
+    }
+
+    public void testGetParentRelative() {
+        DataStore store = getStore();
+        VirtualFile vFile = new VirtualFile(
+                PathUtilTest.join(NON_EXISTING_DIRS), store);
+        int count = 0;
+        StorageFile parent = vFile.getParentDir();
+        while (parent != null) {
+            count++;
+            parent = parent.getParentDir();
+        }
+        assertEquals(4, count);
+    }
+
+    public void testGetParentAbsolute() {
+        DataStore store = getStore();
+        VirtualFile vFile = new VirtualFile(
+                PathUtilTest.joinAbs(NON_EXISTING_DIRS), store);
+        int count = 0;
+        StorageFile parent = vFile.getParentDir();
+        while (parent != null) {
+            count++;
+            parent = parent.getParentDir();
+        }
+        assertEquals(5, count);
+    }
+
+    public void testDeleteAll()
+            throws IOException {
+        DataStore store = getStore();
+        String[] dirs = new String[] {
+            "seg0", PathUtilTest.join("seg0", "dir1"),
+            "seg1", PathUtilTest.join("seg0", "dir2")};
+        for (int i=0; i < dirs.length; i++) {
+            assertTrue(new VirtualFile(dirs[i], store).mkdir());
+        }
+        String[] files = new String[] {
+            PathUtilTest.join("seg0", "f1"),
+            PathUtilTest.join("seg0", "dir1", "f1"),
+            PathUtilTest.join("seg1", "f1"), PathUtilTest.join("seg0","f5")};
+        for (int i=0; i < files.length; i++) {
+            assertTrue(new VirtualFile(files[i], store).createNewFile());
+        }
+        String root = "seg0";
+        VirtualFile rootToDelete = new VirtualFile(root, store);
+        assertTrue(rootToDelete.deleteAll());
+        for (int i=0; i < dirs.length; i++) {
+            assertEquals(!dirs[i].startsWith(root),
+                         new VirtualFile(dirs[i], store).exists());
+        }
+        for (int i=0; i < files.length; i++) {
+            assertEquals(!files[i].startsWith(root),
+                         new VirtualFile(files[i], store).exists());
+        }
+    }
+
+    public void testRenameToSimple() {
+        DataStore store = getStore();
+        VirtualFile vFile = new VirtualFile("originalFile", store);
+        assertFalse(vFile.canWrite());
+        vFile.createNewFile();
+        assertTrue(vFile.canWrite());
+        VirtualFile newFile = new VirtualFile("newFile", store);
+        assertFalse(newFile.exists());
+        assertTrue(vFile.renameTo(newFile));
+        assertFalse(vFile.exists());
+        assertFalse(vFile.canWrite());
+        assertTrue(newFile.exists());
+    }
+
+    public static Test suite() {
+        return new TestSuite(VirtualFileTest.class);
+    }
+
+    private static int dbStoreIndex = 0;
+    private static synchronized DataStore getStore() {
+        return new DataStore("testVFMemDB-" + dbStoreIndex++);
+    }
+}
Index: java/testing/org/apache/derbyTesting/unitTests/junit/BlockedByteArrayTest.java
===================================================================
--- java/testing/org/apache/derbyTesting/unitTests/junit/BlockedByteArrayTest.java	(revision 0)
+++ java/testing/org/apache/derbyTesting/unitTests/junit/BlockedByteArrayTest.java	(revision 0)
@@ -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;
+    }
+}
Index: java/testing/org/apache/derbyTesting/unitTests/junit/_Suite.java
===================================================================
--- java/testing/org/apache/derbyTesting/unitTests/junit/_Suite.java	(revision 740261)
+++ java/testing/org/apache/derbyTesting/unitTests/junit/_Suite.java	(working copy)
@@ -54,6 +54,9 @@
         suite.addTest(AssertFailureTest.suite());
         suite.addTest(InputStreamUtilTest.suite());
         suite.addTest(CharacterStreamDescriptorTest.suite());
+        suite.addTest(BlockedByteArrayTest.suite());
+        suite.addTest(PathUtilTest.suite());
+        suite.addTest(VirtualFileTest.suite());
 
         return suite;
     }
Index: java/testing/org/apache/derbyTesting/unitTests/junit/PathUtilTest.java
===================================================================
--- java/testing/org/apache/derbyTesting/unitTests/junit/PathUtilTest.java	(revision 0)
+++ java/testing/org/apache/derbyTesting/unitTests/junit/PathUtilTest.java	(revision 0)
@@ -0,0 +1,107 @@
+/*
+
+   Derby - Class org.apache.derbyTesting.unitTests.junit.PathUtilTest
+
+   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.File;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.derby.impl.io.vfmem.PathUtil;
+import org.apache.derbyTesting.junit.BaseTestCase;
+
+/**
+ * Basic tests for the {@code PathUtil} class.
+ */
+public class PathUtilTest
+        extends BaseTestCase {
+
+    private static final String SEP = File.separator;
+
+    public PathUtilTest(String name) {
+        super(name);
+    }
+
+    public void testGetParent() {
+        assertNull(PathUtil.getParent(""));
+        assertNull(PathUtil.getParent(File.separator));
+        assertEquals("seg0",
+                PathUtil.getParent(join("seg0","c1.dat")));
+        assertEquals(abs("seg0"),
+                PathUtil.getParent(joinAbs("seg0", "c1.dat")));
+        assertNull(PathUtil.getParent("seg0" + SEP));
+        assertEquals(SEP,
+                PathUtil.getParent(abs("seg0" + SEP)));
+        assertEquals(joinAbs("dir1", "dir2"),
+                PathUtil.getParent(joinAbs("dir1", "dir2", "myFile.txt")));
+    }
+
+    public void testGetBase() {
+        assertEquals("seg0", PathUtil.getBaseName("seg0"));
+        assertEquals("c1.dat",
+                PathUtil.getBaseName(join("seg0","c1.dat")));
+        assertEquals("c1.dat",
+                PathUtil.getBaseName(joinAbs("seg0","c1.dat")));
+        assertEquals("c1.dat",
+                PathUtil.getBaseName(join("aDir", "seg0","c1.dat")));
+        assertEquals("c1.dat",
+                PathUtil.getBaseName(joinAbs("aDir", "seg0","c1.dat")));
+    }
+
+    public static Test suite() {
+        return new TestSuite(PathUtilTest.class, "PathUtilTest suite");
+    }
+
+    // Simple utility methods to join / create paths.
+
+    public static String abs(String e1) {
+        return SEP + e1;
+    }
+    public static String join(String e1, String e2) {
+        return e1 + SEP + e2;
+    }
+
+    public static String joinAbs(String e1, String e2) {
+        return SEP + join(e1, e2);
+    }
+
+    public static String join(String e1, String e2, String e3) {
+        return e1 + SEP + e2 + SEP + e3;
+    }
+
+    public static String joinAbs(String e1, String e2, String e3) {
+        return SEP + join(e1, e2, e3);
+    }
+
+    public static String join(String[] elems) {
+        StringBuffer str = new StringBuffer();
+        for (int i=0; i < elems.length; i++) {
+            str.append(elems[i]);
+            str.append(SEP);
+        }
+        str.deleteCharAt(str.length() -1);
+        return str.toString();
+    }
+
+    public static String joinAbs(String[] elems) {
+        return SEP + join(elems);
+    }
+}
Index: tools/jar/extraDBMSclasses.properties
===================================================================
--- tools/jar/extraDBMSclasses.properties	(revision 740261)
+++ tools/jar/extraDBMSclasses.properties	(working copy)
@@ -91,6 +91,7 @@
 derby.module.database.constantactionactivation=org.apache.derby.impl.sql.execute.ConstantActionActivation
 
 derby.module.store.dsf4=org.apache.derby.impl.io.DirStorageFactory4
+derby.module.store.vfmsf=org.apache.derby.impl.io.VFMemoryStorageFactory
 derby.module.store.isf=org.apache.derby.impl.io.InputStreamFile
 derby.module.store.jardbf=org.apache.derby.impl.io.JarDBFile
 derby.module.store.urlf=org.apache.derby.impl.io.URLFile
