Moving to root below branch label

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2005-12-08 07:13:07 +00:00
commit e1e6508fec
1095 changed files with 230566 additions and 0 deletions

View File

@@ -0,0 +1,235 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.content.filestore;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.text.MessageFormat;
import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.content.AbstractContentReader;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.RandomAccessContent;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentStreamListener;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Provides direct access to a local file.
* <p>
* This class does not provide remote access to the file.
*
* @author Derek Hulley
*/
public class FileContentReader extends AbstractContentReader implements RandomAccessContent
{
private static final Log logger = LogFactory.getLog(FileContentReader.class);
private File file;
/**
* Checks the existing reader provided and replaces it with a reader onto some
* fake content if required. If the existing reader is invalid, an debug message
* will be logged under this classname category.
* <p>
* It is a convenience method that clients can use to cheaply get a reader that
* is valid, regardless of whether the initial reader is valid.
*
* @param existingReader a potentially valid reader
* @param msgTemplate the template message that will used to format the final <i>fake</i> content
* @param args arguments to put into the <i>fake</i> content
* @return Returns a the existing reader or a new reader onto some generated text content
*/
public static ContentReader getSafeContentReader(ContentReader existingReader, String msgTemplate, Object ... args)
{
ContentReader reader = existingReader;
if (existingReader == null || !existingReader.exists())
{
// the content was never written to the node or the underlying content is missing
String fakeContent = MessageFormat.format(msgTemplate, args);
// log it
if (logger.isDebugEnabled())
{
logger.debug(fakeContent);
}
// fake the content
File tempFile = TempFileProvider.createTempFile("getSafeContentReader_", ".txt");
ContentWriter writer = new FileContentWriter(tempFile);
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
writer.setEncoding("UTF-8");
writer.putContent(fakeContent);
// grab the reader from the temp writer
reader = writer.getReader();
}
// done
if (logger.isDebugEnabled())
{
logger.debug("Created safe content reader: \n" +
" existing reader: " + existingReader + "\n" +
" safe reader: " + reader);
}
return reader;
}
/**
* Constructor that builds a URL based on the absolute path of the file.
*
* @param file the file for reading. This will most likely be directly
* related to the content URL.
*/
public FileContentReader(File file)
{
this(file, FileContentStore.STORE_PROTOCOL + file.getAbsolutePath());
}
/**
* Constructor that explicitely sets the URL that the reader represents.
*
* @param file the file for reading. This will most likely be directly
* related to the content URL.
* @param url the relative url that the reader represents
*/
public FileContentReader(File file, String url)
{
super(url);
this.file = file;
}
/**
* @return Returns the file that this reader accesses
*/
public File getFile()
{
return file;
}
public boolean exists()
{
return file.exists();
}
/**
* @see File#length()
*/
public long getSize()
{
if (!exists())
{
return 0L;
}
else
{
return file.length();
}
}
/**
* @see File#lastModified()
*/
public long getLastModified()
{
if (!exists())
{
return 0L;
}
else
{
return file.lastModified();
}
}
/**
* The URL of the write is known from the start and this method contract states
* that no consideration needs to be taken w.r.t. the stream state.
*/
@Override
protected ContentReader createReader() throws ContentIOException
{
return new FileContentReader(this.file, getContentUrl());
}
@Override
protected ReadableByteChannel getDirectReadableChannel() throws ContentIOException
{
try
{
// the file must exist
if (!file.exists())
{
throw new IOException("File does not exist");
}
// create the channel
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); // won't create it
FileChannel channel = randomAccessFile.getChannel();
// done
if (logger.isDebugEnabled())
{
logger.debug("Opened channel to file: " + file);
}
return channel;
}
catch (Throwable e)
{
throw new ContentIOException("Failed to open file channel: " + this, e);
}
}
/**
* @param directChannel a file channel
*/
@Override
protected ReadableByteChannel getCallbackReadableChannel(
ReadableByteChannel directChannel,
List<ContentStreamListener> listeners) throws ContentIOException
{
if (!(directChannel instanceof FileChannel))
{
throw new AlfrescoRuntimeException("Expected read channel to be a file channel");
}
FileChannel fileChannel = (FileChannel) directChannel;
// wrap it
FileChannel callbackChannel = new CallbackFileChannel(fileChannel, listeners);
// done
return callbackChannel;
}
/**
* @return Returns false as this is a reader
*/
public boolean canWrite()
{
return false; // we only allow reading
}
public FileChannel getChannel() throws ContentIOException
{
// go through the super classes to ensure that all concurrency conditions
// and listeners are satisfied
return (FileChannel) super.getReadableChannel();
}
}

View File

@@ -0,0 +1,319 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.content.filestore;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.content.AbstractContentStore;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Provides a store of node content directly to the file system.
* <p>
* The file names obey, as they must, the URL naming convention
* as specified in the {@link org.alfresco.repo.content.ContentStore}.
*
* @author Derek Hulley
*/
public class FileContentStore extends AbstractContentStore
{
private static final Log logger = LogFactory.getLog(FileContentStore.class);
private File rootDirectory;
private String rootAbsolutePath;
/**
* @param rootDirectory the root under which files will be stored. The
* directory will be created if it does not exist.
*/
public FileContentStore(String rootDirectoryStr)
{
rootDirectory = new File(rootDirectoryStr);
if (!rootDirectory.exists())
{
if (!rootDirectory.mkdirs())
{
throw new ContentIOException("Failed to create store root: " + rootDirectory, null);
}
}
rootDirectory = rootDirectory.getAbsoluteFile();
rootAbsolutePath = rootDirectory.getAbsolutePath();
}
public String toString()
{
StringBuilder sb = new StringBuilder(36);
sb.append("FileContentStore")
.append("[ root=").append(rootDirectory)
.append("]");
return sb.toString();
}
/**
* Generates a new URL and file appropriate to it.
*
* @return Returns a new and unique file
* @throws IOException if the file or parent directories couldn't be created
*/
private File createNewFile() throws IOException
{
String contentUrl = createNewUrl();
return createNewFile(contentUrl);
}
/**
* Creates a file for the specifically provided content URL. The URL may
* not already be in use.
* <p>
* The store prefix is stripped off the URL and the rest of the URL
* used directly to create a file.
*
* @param newContentUrl the specific URL to use, which may not be in use
* @return Returns a new and unique file
* @throws IOException if the file or parent directories couldn't be created or
* if the URL is already in use.
*/
public File createNewFile(String newContentUrl) throws IOException
{
File file = makeFile(newContentUrl);
// create the directory, if it doesn't exist
File dir = file.getParentFile();
if (!dir.exists())
{
dir.mkdirs();
}
// create a new, empty file
boolean created = file.createNewFile();
if (!created)
{
throw new ContentIOException(
"When specifying a URL for new content, the URL may not be in use already. \n" +
" store: " + this + "\n" +
" new URL: " + newContentUrl);
}
// done
return file;
}
/**
* Takes the file absolute path, strips off the root path of the store
* and appends the store URL prefix.
*
* @param file the file from which to create the URL
* @return Returns the equivalent content URL
* @throws Exception
*/
private String makeContentUrl(File file)
{
String path = file.getAbsolutePath();
// check if it belongs to this store
if (!path.startsWith(rootAbsolutePath))
{
throw new AlfrescoRuntimeException(
"File does not fall below the store's root: \n" +
" file: " + file + "\n" +
" store: " + this);
}
// strip off the file separator char, if present
int index = rootAbsolutePath.length();
if (path.charAt(index) == File.separatorChar)
{
index++;
}
// strip off the root path and adds the protocol prefix
String url = AbstractContentStore.STORE_PROTOCOL + path.substring(index);
// replace '\' with '/' so that URLs are consistent across all filesystems
url = url.replace('\\', '/');
// done
return url;
}
/**
* Creates a file from the given relative URL. The URL must start with
* the required {@link FileContentStore#STORE_PROTOCOL protocol prefix}.
*
* @param contentUrl the content URL including the protocol prefix
* @return Returns a file representing the URL - the file may or may not
* exist
*
* @see #checkUrl(String)
*/
private File makeFile(String contentUrl)
{
// take just the part after the protocol
String relativeUrl = getRelativePart(contentUrl);
// get the file
File file = new File(rootDirectory, relativeUrl);
// done
return file;
}
/**
* Performs a direct check against the file for its existence.
*/
@Override
public boolean exists(String contentUrl) throws ContentIOException
{
File file = makeFile(contentUrl);
return file.exists();
}
/**
* This implementation requires that the URL start with
* {@link FileContentStore#STORE_PROTOCOL }.
*/
public ContentReader getReader(String contentUrl)
{
try
{
File file = makeFile(contentUrl);
FileContentReader reader = new FileContentReader(file, contentUrl);
// done
if (logger.isDebugEnabled())
{
logger.debug("Created content reader: \n" +
" url: " + contentUrl + "\n" +
" file: " + file + "\n" +
" reader: " + reader);
}
return reader;
}
catch (Throwable e)
{
throw new ContentIOException("Failed to get reader for URL: " + contentUrl, e);
}
}
/**
* @return Returns a writer onto a location based on the date
*/
public ContentWriter getWriter(ContentReader existingContentReader, String newContentUrl)
{
try
{
File file = null;
String contentUrl = null;
if (newContentUrl == null) // a specific URL was not supplied
{
// get a new file with a new URL
file = createNewFile();
// make a URL
contentUrl = makeContentUrl(file);
}
else // the URL has been given
{
file = createNewFile(newContentUrl);
contentUrl = newContentUrl;
}
// create the writer
FileContentWriter writer = new FileContentWriter(file, contentUrl, existingContentReader);
// done
if (logger.isDebugEnabled())
{
logger.debug("Created content writer: \n" +
" writer: " + writer);
}
return writer;
}
catch (IOException e)
{
throw new ContentIOException("Failed to get writer", e);
}
}
public Set<String> getUrls()
{
// recursively get all files within the root
Set<String> contentUrls = new HashSet<String>(1000);
getUrls(rootDirectory, contentUrls);
// done
if (logger.isDebugEnabled())
{
logger.debug("Listed all content URLS: \n" +
" store: " + this + "\n" +
" count: " + contentUrls.size());
}
return contentUrls;
}
/**
* @param directory the current directory to get the files from
* @param contentUrls the list of current content URLs to add to
* @return Returns a list of all files within the given directory and all subdirectories
*/
private void getUrls(File directory, Set<String> contentUrls)
{
File[] files = directory.listFiles();
if (files == null)
{
// the directory has disappeared
throw new ContentIOException("Failed list files in folder: " + directory);
}
for (File file : files)
{
if (file.isDirectory())
{
// we have a subdirectory - recurse
getUrls(file, contentUrls);
}
else
{
// found a file - create the URL
String contentUrl = makeContentUrl(file);
contentUrls.add(contentUrl);
}
}
}
/**
* Attempts to delete the content. The actual deletion is optional on the interface
* so it just returns the success or failure of the underlying delete.
*/
public boolean delete(String contentUrl) throws ContentIOException
{
// ignore files that don't exist
File file = makeFile(contentUrl);
if (!file.exists())
{
return true;
}
// attempt to delete the file directly
boolean deleted = file.delete();
// done
if (logger.isDebugEnabled())
{
logger.debug("Delete content directly: \n" +
" store: " + this + "\n" +
" url: " + contentUrl);
}
return deleted;
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.content.filestore;
import java.io.File;
import org.alfresco.repo.content.AbstractContentReadWriteTest;
import org.alfresco.repo.content.ContentStore;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.util.TempFileProvider;
/**
* Tests read and write functionality for the store.
*
* @see org.alfresco.repo.content.filestore.FileContentStore
*
* @author Derek Hulley
*/
public class FileContentStoreTest extends AbstractContentReadWriteTest
{
private FileContentStore store;
@Override
public void setUp() throws Exception
{
super.setUp();
// create a store that uses a subdirectory of the temp directory
File tempDir = TempFileProvider.getTempDir();
store = new FileContentStore(
tempDir.getAbsolutePath() +
File.separatorChar +
getName());
}
@Override
protected ContentStore getStore()
{
return store;
}
public void testGetSafeContentReader() throws Exception
{
String template = "ABC {0}{1}";
String arg0 = "DEF";
String arg1 = "123";
String fakeContent = "ABC DEF123";
// get a good reader
ContentReader reader = getReader();
assertFalse("No content has been written to the URL yet", reader.exists());
// now create a file for it
File file = store.createNewFile(reader.getContentUrl());
assertTrue("File store did not connect new file", file.exists());
assertTrue("Reader did not detect creation of the underlying file", reader.exists());
// remove the underlying content
file.delete();
assertFalse("File not missing", file.exists());
assertFalse("Reader doesn't show missing content", reader.exists());
// make a safe reader
ContentReader safeReader = FileContentReader.getSafeContentReader(reader, template, arg0, arg1);
// check it
assertTrue("Fake content doesn't exist", safeReader.exists());
assertEquals("Fake content incorrect", fakeContent, safeReader.getContentString());
assertEquals("Fake mimetype incorrect", MimetypeMap.MIMETYPE_TEXT_PLAIN, safeReader.getMimetype());
assertEquals("Fake encoding incorrect", "UTF-8", safeReader.getEncoding());
// now repeat with a null reader
reader = null;
safeReader = FileContentReader.getSafeContentReader(reader, template, arg0, arg1);
// check it
assertTrue("Fake content doesn't exist", safeReader.exists());
assertEquals("Fake content incorrect", fakeContent, safeReader.getContentString());
}
}

View File

@@ -0,0 +1,223 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.content.filestore;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.content.AbstractContentWriter;
import org.alfresco.repo.content.RandomAccessContent;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentStreamListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Provides direct access to a local file.
* <p>
* This class does not provide remote access to the file.
*
* @author Derek Hulley
*/
public class FileContentWriter extends AbstractContentWriter implements RandomAccessContent
{
private static final Log logger = LogFactory.getLog(FileContentWriter.class);
private File file;
/**
* Constructor that builds a URL based on the absolute path of the file.
*
* @param file the file for writing. This will most likely be directly
* related to the content URL.
*/
public FileContentWriter(File file)
{
this(
file,
FileContentStore.STORE_PROTOCOL + file.getAbsolutePath(),
null);
}
/**
* Constructor that builds a URL based on the absolute path of the file.
*
* @param file the file for writing. This will most likely be directly
* related to the content URL.
* @param existingContentReader a reader of a previous version of this content
*/
public FileContentWriter(File file, ContentReader existingContentReader)
{
this(
file,
FileContentStore.STORE_PROTOCOL + file.getAbsolutePath(),
existingContentReader);
}
/**
* Constructor that explicitely sets the URL that the reader represents.
*
* @param file the file for writing. This will most likely be directly
* related to the content URL.
* @param url the relative url that the reader represents
* @param existingContentReader a reader of a previous version of this content
*/
public FileContentWriter(File file, String url, ContentReader existingContentReader)
{
super(url, existingContentReader);
this.file = file;
}
/**
* @return Returns the file that this writer accesses
*/
public File getFile()
{
return file;
}
/**
* @return Returns the size of the underlying file or
*/
public long getSize()
{
if (file == null)
return 0L;
else if (!file.exists())
return 0L;
else
return file.length();
}
/**
* The URL of the write is known from the start and this method contract states
* that no consideration needs to be taken w.r.t. the stream state.
*/
@Override
protected ContentReader createReader() throws ContentIOException
{
return new FileContentReader(this.file, getContentUrl());
}
@Override
protected WritableByteChannel getDirectWritableChannel() throws ContentIOException
{
try
{
// we may not write to an existing file - EVER!!
if (file.exists() && file.length() > 0)
{
throw new IOException("File exists - overwriting not allowed");
}
// create the channel
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); // will create it
FileChannel channel = randomAccessFile.getChannel();
// done
if (logger.isDebugEnabled())
{
logger.debug("Opened channel to file: " + file);
}
return channel;
}
catch (Throwable e)
{
throw new ContentIOException("Failed to open file channel: " + this, e);
}
}
/**
* @param directChannel a file channel
*/
@Override
protected WritableByteChannel getCallbackWritableChannel(
WritableByteChannel directChannel,
List<ContentStreamListener> listeners) throws ContentIOException
{
if (!(directChannel instanceof FileChannel))
{
throw new AlfrescoRuntimeException("Expected write channel to be a file channel");
}
FileChannel fileChannel = (FileChannel) directChannel;
// wrap it
FileChannel callbackChannel = new CallbackFileChannel(fileChannel, listeners);
// done
return callbackChannel;
}
/**
* @return Returns true always
*/
public boolean canWrite()
{
return true; // this is a writer
}
public FileChannel getChannel() throws ContentIOException
{
/*
* By calling this method, clients indicate that they wish to make random
* changes to the file. It is possible that the client might only want
* to update a tiny proportion of the file - or even none of it. Either
* way, the file must be as whole and complete as before it was accessed.
*/
// go through the super classes to ensure that all concurrency conditions
// and listeners are satisfied
FileChannel channel = (FileChannel) super.getWritableChannel();
// random access means that the the new content's starting point must be
// that of the existing content
ContentReader existingContentReader = getExistingContentReader();
if (existingContentReader != null)
{
ReadableByteChannel existingContentChannel = existingContentReader.getReadableChannel();
long existingContentLength = existingContentReader.getSize();
// copy the existing content
try
{
channel.transferFrom(existingContentChannel, 0, existingContentLength);
// copy complete
if (logger.isDebugEnabled())
{
logger.debug("Copied content for random access: \n" +
" writer: " + this + "\n" +
" existing: " + existingContentReader);
}
}
catch (IOException e)
{
throw new ContentIOException("Failed to copy from existing content to enable random access: \n" +
" writer: " + this + "\n" +
" existing: " + existingContentReader,
e);
}
finally
{
try { existingContentChannel.close(); } catch (IOException e) {}
}
}
// the file is now available for random access
return channel;
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.content.filestore;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import junit.framework.TestCase;
/**
* Some tests to check out the </code>java.lang.nio</code> functionality
*
* @author Derek Hulley
*/
public class FileIOTest extends TestCase
{
private static final String TEST_CONTENT = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private File file;
public FileIOTest(String name)
{
super(name);
}
public void setUp() throws Exception
{
file = File.createTempFile(getName(), ".txt");
OutputStream os = new FileOutputStream(file);
os.write(TEST_CONTENT.getBytes());
os.flush();
os.close();
}
/**
* Attempt to read the same file using multiple channels concurrently
*/
public void testConcurrentFileReads() throws Exception
{
// open the file for a read
FileInputStream isA = new FileInputStream(file);
FileInputStream isB = new FileInputStream(file);
// get the channels
FileChannel channelA = isA.getChannel();
FileChannel channelB = isB.getChannel();
// buffers for reading
ByteBuffer bufferA = ByteBuffer.allocate(10);
ByteBuffer bufferB = ByteBuffer.allocate(10);
// read file into both buffers
int countA = 0;
int countB = 0;
do
{
countA = channelA.read((ByteBuffer)bufferA.clear());
countB = channelB.read((ByteBuffer)bufferB.clear());
assertEquals("Should read same number of bytes", countA, countB);
} while (countA > 6);
// both buffers should be at the same marker 6
assertEquals("BufferA marker incorrect", 6, bufferA.position());
assertEquals("BufferB marker incorrect", 6, bufferB.position());
}
public void testConcurrentReadWrite() throws Exception
{
// open file for a read
FileInputStream isRead = new FileInputStream(file);
// open file for write
FileOutputStream osWrite = new FileOutputStream(file);
// get channels
FileChannel channelRead = isRead.getChannel();
FileChannel channelWrite = osWrite.getChannel();
// buffers
ByteBuffer bufferRead = ByteBuffer.allocate(26);
ByteBuffer bufferWrite = ByteBuffer.wrap(TEST_CONTENT.getBytes());
// read - nothing will be read
int countRead = channelRead.read(bufferRead);
assertEquals("Expected nothing to be read", -1, countRead);
// write
int countWrite = channelWrite.write(bufferWrite);
assertEquals("Not all characters written", 26, countWrite);
// close the write side
channelWrite.close();
// reread
countRead = channelRead.read(bufferRead);
assertEquals("Expected full read", 26, countRead);
}
}