Java tutorial
/* Copyright (C) 2013-2014 Ian Teune <ian.teune@gmail.com> * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.tinspx.util.io; import static com.google.common.base.Preconditions.*; import static com.tinspx.util.io.ChannelSource.*; import com.google.common.base.Charsets; import com.google.common.base.Function; import com.google.common.io.ByteSource; import com.google.common.io.Closer; import com.google.common.io.Files; import com.google.common.io.Resources; import java.io.Closeable; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.Arrays; import java.util.Random; import javax.annotation.Nullable; import lombok.NonNull; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; /** * * @author Ian */ public class ChannelSourceTest { private static byte[] INPUT; private static byte[] EMPTY; public ChannelSourceTest() { } @BeforeClass public static void setUpClass() { INPUT = IOTest.getRandomBytes(new Random(), 1024 * 64, Charsets.UTF_8); EMPTY = new byte[INPUT.length]; } @AfterClass public static void tearDownClass() { INPUT = null; EMPTY = null; } @Before public void setUp() { } @After public void tearDown() { } @Test public void testEmptySource() throws IOException { ByteSourceTests.assertEmpty(ChannelSource.empty()); ByteSourceTests.assertEmpty(ByteSource.empty()); } @Test public void testBAChannelSource() throws IOException { ChannelSource s = ChannelSource.of(INPUT); assertTrue(s.hasKnownSize()); assertEquals(INPUT.length, s.size()); ByteSourceTests.testByteSource(s, INPUT); int off = 443, len = 17167; s = ChannelSource.of(INPUT, off, len); assertTrue(s.hasKnownSize()); assertEquals(len, s.size()); ByteSourceTests.testByteSource(s, Arrays.copyOfRange(INPUT, off, off + len)); } @Test public void testWrap() throws IOException { ByteSourceTests.testByteSource(ChannelSource.of(ByteSource.wrap(INPUT)), INPUT); ByteSourceTests.testByteSource(ChannelSource.of(Forced.byteSource(ChannelSource.of(INPUT))), INPUT); ByteSourceTests.testByteSource( ChannelSource.of(Forced.byteSource(new ByteSourceTests.ForcedByteArray(INPUT))), INPUT); ByteSourceTests.testByteSource( ChannelSource.of(Forced.byteSource(ChannelSource.of(ByteBuffer.wrap(INPUT)))), INPUT); ByteSourceTests.testByteSource( ChannelSource.of(Forced.byteSource(ChannelSource.of(ByteBuffer.wrap(INPUT).asReadOnlyBuffer()))), INPUT); } @Test public void testByteBufferSource() throws IOException { int off = 443, len = 17167; ByteBuffer buf, direct; direct = ByteBuffer.allocateDirect(INPUT.length); assertTrue(direct.isDirect()); direct.put(INPUT); byte[] sub = Arrays.copyOfRange(INPUT, off, off + len); //full input buf = ByteBuffer.wrap(INPUT); ByteSourceTests.testByteSource(ChannelSource.of(buf), INPUT); assertEquals(0, buf.position()); assertEquals(INPUT.length, buf.limit()); buf = ByteBuffer.wrap(INPUT).asReadOnlyBuffer(); ByteSourceTests.testByteSource(ChannelSource.of(buf), INPUT); assertEquals(0, buf.position()); assertEquals(INPUT.length, buf.limit()); direct.clear(); buf = direct; ByteSourceTests.testByteSource(ChannelSource.of(buf), INPUT); assertEquals(0, buf.position()); assertEquals(INPUT.length, buf.limit()); //sub range of input buf = ByteBuffer.wrap(INPUT); buf.clear().position(off).limit(off + len); ByteSourceTests.testByteSource(ChannelSource.of(buf), sub); assertEquals(off, buf.position()); assertEquals(off + len, buf.limit()); buf = ByteBuffer.wrap(INPUT).asReadOnlyBuffer(); buf.clear().position(off).limit(off + len); ByteSourceTests.testByteSource(ChannelSource.of(buf), sub); assertEquals(off, buf.position()); assertEquals(off + len, buf.limit()); direct.clear(); buf = direct; buf.clear().position(off).limit(off + len); ByteSourceTests.testByteSource(ChannelSource.of(buf), sub); assertEquals(off, buf.position()); assertEquals(off + len, buf.limit()); } @Test public void testMemoizeSource() throws IOException { ByteSource source = of(ByteBuffer.allocate(16)); assertSame(source, ChannelSource.memoize(source)); source = of(new byte[16]); assertSame(source, ChannelSource.memoize(source)); source = memoize(ByteSource.empty()); assertSame(source, source); source = memoize(ByteSourceTests.force(of(INPUT))); assertTrue(source instanceof ChannelSource.MemoizeChannelSource); ByteSourceTests.testByteSource(source, INPUT); source = memoize(ByteSourceTests.force(of(ByteBuffer.wrap(INPUT)))); assertTrue(source instanceof ChannelSource.MemoizeChannelSource); ByteSourceTests.testByteSource(source, INPUT); source = memoize(ByteSourceTests.force(of(ByteBuffer.wrap(INPUT).asReadOnlyBuffer()))); assertTrue(source instanceof ChannelSource.MemoizeChannelSource); ByteSourceTests.testByteSource(source, INPUT); ByteBuffer direct = ByteBuffer.allocateDirect(INPUT.length); assertTrue(direct.isDirect()); direct.put(INPUT).flip(); source = memoize(ByteSourceTests.force(of(direct))); assertTrue(source instanceof ChannelSource.MemoizeChannelSource); ByteSourceTests.testByteSource(source, INPUT); source = memoize(new BAOutputStream(INPUT, INPUT.length).asByteSource()); assertTrue(source instanceof ChannelSource.MemoizeChannelSource); ByteSourceTests.testByteSource(source, INPUT); source = memoize(new ByteSourceTests.ForcedByteArray(INPUT)); assertTrue(source instanceof ChannelSource.MemoizeChannelSource); ByteSourceTests.testByteSource(source, INPUT); source = memoize(ByteSource.wrap(INPUT)); assertTrue(source instanceof ChannelSource.MemoizeChannelSource); ByteSourceTests.testByteSource(source, INPUT); ByteSourceTests.assertEmpty(memoize(ByteSource.empty())); ByteSourceTests.assertEmpty(memoize(ChannelSource.empty())); } static final String FORMATTED = "formatted.txt"; static final String RAW = "raw.txt"; static final String SAMPLE = "json_sample.txt"; static File OUTPUT; static File outputFile() { if (OUTPUT == null) { OUTPUT = new File(new File(System.getProperty("user.dir")), "test/output.txt"); } return OUTPUT; } /** * checks the output file against {@code ref}, then deletes it */ @SuppressWarnings("ThrowFromFinallyBlock") static void checkOutputFile(byte[] ref) throws IOException { try { assertArrayEquals(ref, ByteUtils.toByteArray(outputFile())); } finally { if (!outputFile().delete()) { throw new IOException("could not delete: " + outputFile()); } } } static void deleteOutputFile() { if (outputFile().exists()) { if (!outputFile().delete()) { throw new RuntimeException("could not delete: " + outputFile()); } } } static File setupTestDir() throws IOException { File testDir = new File(new File(System.getProperty("user.dir")), "test"); if (testDir.exists()) { if (testDir.list().length > 0) { throw new IOException(testDir + " exists and is not empty"); } } else if (!testDir.mkdirs()) { throw new IOException("could not create " + testDir); } boolean complete = false; try { Resources.asByteSource(ChannelSource.class.getResource("/" + FORMATTED)) .copyTo(Files.asByteSink(new File(testDir, FORMATTED))); Resources.asByteSource(ChannelSource.class.getResource("/" + SAMPLE)) .copyTo(Files.asByteSink(new File(testDir, SAMPLE))); complete = true; return testDir; } finally { if (!complete) { teardownTestDir(testDir).close(); } } } static Closeable teardownTestDir(@NonNull final File testDir) throws IOException { return new Closeable() { @Override public void close() throws IOException { FileUtils.deleteDirectory(testDir); } }; } @Test public void testFileSource() throws IOException { File testDir = setupTestDir(); Closer closer = Closer.create(); try { closer.register(teardownTestDir(testDir)); testFileSource(new File(testDir, FORMATTED)); testFileSource(new File(testDir, SAMPLE)); } catch (Throwable t) { throw closer.rethrow(t); } finally { closer.close(); } } static void testFileSource(File file) throws IOException { System.out.printf("Testing %s; Length: %s%n", file, file.length()); byte[] ref = ByteUtils.toByteArray(file); testFileSource(ref, file, null); testFileSource(ref, file, fromRandomAccessFile()); testFileSource(ref, file, fromRandomAccessFile("r")); testFileSource(ref, file, fromRandomAccessFile("rws")); testFileSource(ref, file, fromRandomAccessFile("rwd")); } static void checkOutputFileDNE() { checkState(!outputFile().exists(), "(%s) exists", outputFile()); } static void testFileSource(byte[] ref, File file, @Nullable Function<File, FileChannel> function) throws IOException { Closer closer; ChannelSource s; if (function != null) { s = of(file, function); } else { s = of(file); } ByteSourceTests.testByteSource(s, ref); assertTrue(s.hasKnownSize()); assertEquals(file.length(), s.size()); //normal FileOutputStream checkOutputFileDNE(); closer = Closer.create(); try { assertEquals(ref.length, s.copyTo(closer.register(new FileOutputStream(outputFile())))); } catch (Throwable t) { throw closer.rethrow(t); } finally { closer.close(); } checkOutputFile(ref); //force FileOutputStream as OutputStream checkOutputFileDNE(); closer = Closer.create(); try { assertEquals(ref.length, s.copyTo(closer.register(Forced.outputStream(new FileOutputStream(outputFile()))))); } catch (Throwable t) { throw closer.rethrow(t); } finally { closer.close(); } checkOutputFile(ref); //copyTo a WritableByteChannel checkOutputFileDNE(); closer = Closer.create(); try { //ByteUtils.asWritableByteChannel extends OutputStream (actually turned out to be usefull) OutputStream out = (OutputStream) ByteUtils .asWritableByteChannel(closer.register(new FileOutputStream(outputFile()))); assert (out instanceof ByteUtils.OutputStreamWritableByteChannel); assertEquals(ref.length, s.copyTo(out)); } catch (Throwable t) { throw closer.rethrow(t); } finally { closer.close(); } checkOutputFile(ref); checkOutputFileDNE(); assertEquals(ref.length, s.copyTo(ChannelSink.of(outputFile()))); checkOutputFile(ref); checkOutputFileDNE(); assertEquals(ref.length, s.copyTo(ChannelSink.of(outputFile(), fromRandomAccessFile()))); checkOutputFile(ref); checkOutputFileDNE(); assertEquals(ref.length, s.copyTo(ChannelSink.of(outputFile(), fromRandomAccessFile("rws")))); checkOutputFile(ref); checkOutputFileDNE(); assertEquals(ref.length, s.copyTo(ChannelSink.of(outputFile(), fromRandomAccessFile("rwd")))); checkOutputFile(ref); //test weird channel and sink BAOutputStream out = new BAOutputStream(ref.length); assertEquals(ref.length, ((ChannelSource.FileChannelSource) s).copyTo(new WeirdWritableChannel(out))); assertArrayEquals(ref, out.backingToByteArray()); out.clearAndReset(); assertEquals(ref.length, s.copyTo(new WeirdChannelSink(out.asByteSink()))); assertArrayEquals(ref, out.backingToByteArray()); } static class WeirdChannelSink extends ChannelSink { final ChannelSink delegate; public WeirdChannelSink(ChannelSink delegate) { this.delegate = checkNotNull(delegate); } @Override public WritableByteChannel openChannel() throws IOException { return new WeirdWritableChannel(delegate.openChannel()); } } static class WeirdWritableChannel implements WritableByteChannel { final WritableByteChannel delegate; int count; public WeirdWritableChannel(WritableByteChannel delegate) { this.delegate = checkNotNull(delegate); } @SuppressWarnings("fallthrough") @Override public int write(ByteBuffer dst) throws IOException { switch (count++ % 5) { case 0: case 3: //don't allow writing return 0; case 1: case 2: //only allow half of dst to be used if (dst.remaining() > 1) { final int limit = dst.limit(); dst.limit(dst.position() + (dst.remaining() / 2)); int w = delegate.write(dst); dst.limit(limit); return w; } //fall through case 4: //normal write return delegate.write(dst); default: throw new AssertionError(); } } @Override public boolean isOpen() { return delegate.isOpen(); } @Override public void close() throws IOException { delegate.close(); } } }