1/*
2 * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/* @test
25 * @bug 4429043 8002180
26 * @summary Test file mapping with FileChannel
27 * @run main/othervm MapTest
28 * @key randomness
29 */
30
31import java.io.*;
32import java.nio.MappedByteBuffer;
33import java.nio.channels.*;
34import java.nio.channels.FileChannel.MapMode;
35import java.nio.file.Files;
36import static java.nio.file.StandardOpenOption.*;
37import static java.nio.charset.StandardCharsets.*;
38import java.util.Random;
39
40
41/**
42 * Testing FileChannel's mapping capabilities.
43 */
44
45public class MapTest {
46
47    private static PrintStream out = System.out;
48    private static PrintStream err = System.err;
49
50    private static Random generator = new Random();
51
52    private static int CHARS_PER_LINE = File.separatorChar == '/' ? 5 : 6;
53
54    private static File blah;
55
56    public static void main(String[] args) throws Exception {
57        blah = File.createTempFile("blah", null);
58        blah.deleteOnExit();
59        initTestFile(blah);
60        try {
61            out.println("Test file " + blah + " initialized");
62            testZero();
63            out.println("Zero size: OK");
64            testRead();
65            out.println("Read: OK");
66            testWrite();
67            out.println("Write: OK");
68            testHighOffset();
69            out.println("High offset: OK");
70            testExceptions();
71            out.println("Exceptions: OK");
72        } finally {
73            blah.delete();
74        }
75    }
76
77    /**
78     * Creates file blah:
79     * 0000
80     * 0001
81     * 0002
82     * 0003
83     * .
84     * .
85     * .
86     * 3999
87     *
88     * Blah extends beyond a single page of memory so that the
89     * ability to index into a file of multiple pages is tested.
90     */
91    private static void initTestFile(File blah) throws Exception {
92        try (BufferedWriter writer = Files.newBufferedWriter(blah.toPath(), ISO_8859_1)) {
93            for (int i=0; i<4000; i++) {
94                String number = new Integer(i).toString();
95                for (int h=0; h<4-number.length(); h++)
96                    writer.write("0");
97                writer.write(""+i);
98                writer.newLine();
99            }
100        }
101    }
102
103    /**
104     * Tests zero size file mapping
105     */
106    private static void testZero() throws Exception {
107        try (FileInputStream fis = new FileInputStream(blah)) {
108            FileChannel fc = fis.getChannel();
109            MappedByteBuffer b = fc.map(MapMode.READ_ONLY, 0, 0);
110        }
111    }
112
113    /**
114     * Maps blah file with a random offset and checks to see if read
115     * from the ByteBuffer gets the right line number
116     */
117    private static void testRead() throws Exception {
118        StringBuilder sb = new StringBuilder();
119        sb.setLength(4);
120
121        for (int x=0; x<1000; x++) {
122            try (FileInputStream fis = new FileInputStream(blah)) {
123                FileChannel fc = fis.getChannel();
124
125                long offset = generator.nextInt(10000);
126                long expectedResult = offset / CHARS_PER_LINE;
127                offset = expectedResult * CHARS_PER_LINE;
128
129                MappedByteBuffer b = fc.map(MapMode.READ_ONLY,
130                                            offset, 100);
131
132                for (int i=0; i<4; i++) {
133                    byte aByte = b.get(i);
134                    sb.setCharAt(i, (char)aByte);
135                }
136
137                int result = Integer.parseInt(sb.toString());
138                if (result != expectedResult) {
139                    err.println("I expected "+expectedResult);
140                    err.println("I got "+result);
141                    throw new Exception("Read test failed");
142                }
143            }
144        }
145    }
146
147    /**
148     * Maps blah file with a random offset and checks to see if data
149     * written out to the file can be read back in
150     */
151    private static void testWrite() throws Exception {
152        StringBuilder sb = new StringBuilder();
153        sb.setLength(4);
154
155        for (int x=0; x<1000; x++) {
156            try (RandomAccessFile raf = new RandomAccessFile(blah, "rw")) {
157                FileChannel fc = raf.getChannel();
158
159                long offset = generator.nextInt(1000);
160                MappedByteBuffer b = fc.map(MapMode.READ_WRITE,
161                                            offset, 100);
162
163                for (int i=0; i<4; i++) {
164                    b.put(i, (byte)('0' + i));
165                }
166
167                for (int i=0; i<4; i++) {
168                    byte aByte = b.get(i);
169                    sb.setCharAt(i, (char)aByte);
170                }
171                if (!sb.toString().equals("0123"))
172                    throw new Exception("Write test failed");
173            }
174        }
175    }
176
177    private static void testHighOffset() throws Exception {
178        StringBuilder sb = new StringBuilder();
179        sb.setLength(4);
180
181        for (int x=0; x<1000; x++) {
182            try (RandomAccessFile raf = new RandomAccessFile(blah, "rw")) {
183                FileChannel fc = raf.getChannel();
184                long offset = 66000;
185                MappedByteBuffer b = fc.map(MapMode.READ_WRITE,
186                                            offset, 100);
187            }
188        }
189    }
190
191    /**
192     * Test exceptions specified by map method
193     */
194    private static void testExceptions() throws Exception {
195        // check exceptions when channel opened for read access
196        try (FileChannel fc = FileChannel.open(blah.toPath(), READ)) {
197            testExceptions(fc);
198
199            checkException(fc, MapMode.READ_WRITE, 0L, fc.size(),
200                           NonWritableChannelException.class);
201
202            checkException(fc, MapMode.READ_WRITE, -1L, fc.size(),
203                           NonWritableChannelException.class, IllegalArgumentException.class);
204
205            checkException(fc, MapMode.READ_WRITE, 0L, -1L,
206                           NonWritableChannelException.class, IllegalArgumentException.class);
207
208            checkException(fc, MapMode.PRIVATE, 0L, fc.size(),
209                           NonWritableChannelException.class);
210
211            checkException(fc, MapMode.PRIVATE, -1L, fc.size(),
212                           NonWritableChannelException.class, IllegalArgumentException.class);
213
214            checkException(fc, MapMode.PRIVATE, 0L, -1L,
215                           NonWritableChannelException.class, IllegalArgumentException.class);
216        }
217
218        // check exceptions when channel opened for write access
219        try (FileChannel fc = FileChannel.open(blah.toPath(), WRITE)) {
220            testExceptions(fc);
221
222            checkException(fc, MapMode.READ_ONLY, 0L, fc.size(),
223                           NonReadableChannelException.class);
224
225            checkException(fc, MapMode.READ_ONLY, -1L, fc.size(),
226                           NonReadableChannelException.class, IllegalArgumentException.class);
227
228            /*
229             * implementation/spec mismatch, these tests disabled for now
230             */
231            //checkException(fc, MapMode.READ_WRITE, 0L, fc.size(),
232            //               NonWritableChannelException.class);
233            //checkException(fc, MapMode.PRIVATE, 0L, fc.size(),
234            //               NonWritableChannelException.class);
235        }
236
237        // check exceptions when channel opened for read and write access
238        try (FileChannel fc = FileChannel.open(blah.toPath(), READ, WRITE)) {
239            testExceptions(fc);
240        }
241    }
242
243    private static void testExceptions(FileChannel fc) throws IOException {
244        checkException(fc, null, 0L, fc.size(),
245                       NullPointerException.class);
246
247        checkException(fc, MapMode.READ_ONLY, -1L, fc.size(),
248                       IllegalArgumentException.class);
249
250        checkException(fc, null, -1L, fc.size(),
251                       IllegalArgumentException.class, NullPointerException.class);
252
253        checkException(fc, MapMode.READ_ONLY, 0L, -1L,
254                       IllegalArgumentException.class);
255
256        checkException(fc, null, 0L, -1L,
257                       IllegalArgumentException.class, NullPointerException.class);
258
259        checkException(fc, MapMode.READ_ONLY, 0L, Integer.MAX_VALUE + 1L,
260                       IllegalArgumentException.class);
261
262        checkException(fc, null, 0L, Integer.MAX_VALUE + 1L,
263                       IllegalArgumentException.class, NullPointerException.class);
264
265        checkException(fc, MapMode.READ_ONLY, Long.MAX_VALUE, 1L,
266                       IllegalArgumentException.class);
267
268        checkException(fc, null, Long.MAX_VALUE, 1L,
269                       IllegalArgumentException.class, NullPointerException.class);
270
271    }
272
273    /**
274     * Checks that FileChannel map throws one of the expected exceptions
275     * when invoked with the given inputs.
276     */
277    private static void checkException(FileChannel fc,
278                                       MapMode mode,
279                                       long position,
280                                       long size,
281                                       Class<?>... expected)
282        throws IOException
283    {
284        Exception exc = null;
285        try {
286            fc.map(mode, position, size);
287        } catch (Exception actual) {
288            exc = actual;
289        }
290        if (exc != null) {
291            for (Class<?> clazz: expected) {
292                if (clazz.isInstance(exc)) {
293                    return;
294                }
295            }
296        }
297        System.err.println("Expected one of");
298        for (Class<?> clazz: expected) {
299            System.out.println(clazz);
300        }
301        if (exc == null) {
302            throw new RuntimeException("No expection thrown");
303        } else {
304            throw new RuntimeException("Unexpected exception thrown", exc);
305        }
306    }
307}
308