1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package org.apache.harmony.archive.tests.java.util.jar;
18
19import java.io.ByteArrayOutputStream;
20import java.io.File;
21import java.io.FileOutputStream;
22import java.io.IOException;
23import java.io.InputStream;
24import java.net.URL;
25import java.security.cert.Certificate;
26import java.util.Enumeration;
27import java.util.Vector;
28import java.util.jar.Attributes;
29import java.util.jar.JarEntry;
30import java.util.jar.JarFile;
31import java.util.jar.JarOutputStream;
32import java.util.jar.Manifest;
33import java.util.zip.ZipEntry;
34
35import junit.framework.TestCase;
36import tests.support.Support_PlatformFile;
37import tests.support.resource.Support_Resources;
38
39public class JarFileTest extends TestCase {
40
41    /**
42     * The file contains the following entries:
43     *
44     * <pre>
45     * META-INF/ META-INF/MANIFEST.MF
46     * foo/ foo/bar/ foo/bar/A.class
47     * Blah.txt
48     * </pre>
49     */
50    private final String JAR1 = "hyts_patch.jar";
51
52    private final String JAR2 = "hyts_patch2.jar";
53
54    private final String JAR3 = "hyts_manifest1.jar";
55
56    private final String JAR4 = "hyts_signed.jar";
57
58    private final String JAR5 = "Integrate.jar";
59
60    private final String JAR1_ENTRY1 = "foo/bar/A.class";
61
62    private final String JAR5_SIGNED_ENTRY = "Test.class";
63
64    private final String JAR4_SIGNED_ENTRY = "coucou/FileAccess.class";
65
66    private File resources;
67
68    private final String jarName = "hyts_patch.jar"; // a 'normal' jar file
69
70    private final String entryName = "foo/bar/A.class";
71
72    private final String emptyEntryJar = "EmptyEntries_signed.jar";
73
74    private final String emptyEntry1 = "subfolder/internalSubset01.js";
75
76    private final String emptyEntry2 = "svgtest.js";
77
78    private final String emptyEntry3 = "svgunit.js";
79
80    @Override
81    protected void setUp() {
82        resources = Support_Resources.createTempFolder();
83    }
84
85    /**
86     * Constructs JarFile object.
87     *
88     * @tests java.util.jar.JarFile#JarFile(java.io.File)
89     * @tests java.util.jar.JarFile#JarFile(java.lang.String)
90     */
91    public void testConstructor() throws IOException {
92        File f = new File(resources, JAR1);
93        Support_Resources.copyFile(resources, null, JAR1);
94        assertTrue(new JarFile(f).getEntry(JAR1_ENTRY1).getName().equals(
95                JAR1_ENTRY1));
96        assertTrue(new JarFile(f.getPath()).getEntry(JAR1_ENTRY1).getName()
97                .equals(JAR1_ENTRY1));
98    }
99
100    /**
101     * @tests java.util.jar.JarFile#entries()
102     */
103    public void testEntries() throws Exception {
104        Support_Resources.copyFile(resources, null, JAR1);
105        JarFile jarFile = new JarFile(new File(resources, JAR1));
106        Enumeration<JarEntry> e = jarFile.entries();
107        int i;
108        for (i = 0; e.hasMoreElements(); i++) {
109            e.nextElement();
110        }
111        assertEquals(jarFile.size(), i);
112        jarFile.close();
113        assertEquals(6, i);
114    }
115
116    public void testEntriesIterator() throws Exception {
117        Support_Resources.copyFile(resources, null, JAR1);
118        JarFile jarFile = new JarFile(new File(resources, JAR1));
119        Enumeration<JarEntry> enumeration = jarFile.entries();
120        jarFile.close();
121        try {
122            enumeration.hasMoreElements();
123            fail("hasMoreElements() did not detect a closed jar file");
124        } catch (IllegalStateException e) {
125        }
126        Support_Resources.copyFile(resources, null, JAR1);
127        jarFile = new JarFile(new File(resources, JAR1));
128        enumeration = jarFile.entries();
129        jarFile.close();
130        try {
131            enumeration.nextElement();
132            fail("nextElement() did not detect closed jar file");
133        } catch (IllegalStateException e) {
134        }
135    }
136
137    public void test_getEntryLjava_lang_String() throws IOException {
138        try {
139            Support_Resources.copyFile(resources, null, jarName);
140            JarFile jarFile = new JarFile(new File(resources, jarName));
141            assertEquals("Error in returned entry", 311, jarFile.getEntry(
142                    entryName).getSize());
143            jarFile.close();
144        } catch (Exception e) {
145            fail("Exception during test: " + e.toString());
146        }
147
148        Support_Resources.copyFile(resources, null, jarName);
149        JarFile jarFile = new JarFile(new File(resources, jarName));
150        Enumeration<JarEntry> enumeration = jarFile.entries();
151        assertTrue(enumeration.hasMoreElements());
152        while (enumeration.hasMoreElements()) {
153            JarEntry je = enumeration.nextElement();
154            jarFile.getEntry(je.getName());
155        }
156
157        enumeration = jarFile.entries();
158        assertTrue(enumeration.hasMoreElements());
159        JarEntry je = enumeration.nextElement();
160        try {
161            jarFile.close();
162            jarFile.getEntry(je.getName());
163            // fail("IllegalStateException expected.");
164        } catch (IllegalStateException ee) { // Per documentation exception
165            // may be thrown.
166            // expected
167        }
168    }
169
170    public void test_getJarEntryLjava_lang_String() throws IOException {
171        try {
172            Support_Resources.copyFile(resources, null, jarName);
173            JarFile jarFile = new JarFile(new File(resources, jarName));
174            assertEquals("Error in returned entry", 311, jarFile.getJarEntry(
175                    entryName).getSize());
176            jarFile.close();
177        } catch (Exception e) {
178            fail("Exception during test: " + e.toString());
179        }
180
181        Support_Resources.copyFile(resources, null, jarName);
182        JarFile jarFile = new JarFile(new File(resources, jarName));
183        Enumeration<JarEntry> enumeration = jarFile.entries();
184        assertTrue(enumeration.hasMoreElements());
185        while (enumeration.hasMoreElements()) {
186            JarEntry je = enumeration.nextElement();
187            jarFile.getJarEntry(je.getName());
188        }
189
190        enumeration = jarFile.entries();
191        assertTrue(enumeration.hasMoreElements());
192        JarEntry je = enumeration.nextElement();
193        try {
194            jarFile.close();
195            jarFile.getJarEntry(je.getName());
196            // fail("IllegalStateException expected.");
197        } catch (IllegalStateException ee) { // Per documentation exception
198            // may be thrown.
199            // expected
200        }
201    }
202
203    /**
204     * @tests java.util.jar.JarFile#getJarEntry(java.lang.String)
205     */
206    public void testGetJarEntry() throws Exception {
207        Support_Resources.copyFile(resources, null, JAR1);
208        JarFile jarFile = new JarFile(new File(resources, JAR1));
209        assertEquals("Error in returned entry", 311, jarFile.getEntry(
210                JAR1_ENTRY1).getSize());
211        jarFile.close();
212
213        // tests for signed jars
214        // test all signed jars in the /Testres/Internal/SignedJars directory
215        String jarDirUrl = Support_Resources
216                .getResourceURL("/../internalres/signedjars");
217        Vector<String> signedJars = new Vector<String>();
218        try {
219            InputStream is = new URL(jarDirUrl + "/jarlist.txt").openStream();
220            while (is.available() > 0) {
221                StringBuilder linebuff = new StringBuilder(80); // Typical line
222                // length
223                done: while (true) {
224                    int nextByte = is.read();
225                    switch (nextByte) {
226                    case -1:
227                        break done;
228                    case (byte) '\r':
229                        if (linebuff.length() == 0) {
230                            // ignore
231                        }
232                        break done;
233                    case (byte) '\n':
234                        if (linebuff.length() == 0) {
235                            // ignore
236                        }
237                        break done;
238                    default:
239                        linebuff.append((char) nextByte);
240                    }
241                }
242                if (linebuff.length() == 0) {
243                    break;
244                }
245                String line = linebuff.toString();
246                signedJars.add(line);
247            }
248            is.close();
249        } catch (IOException e) {
250            // no list of jars found
251        }
252
253        for (int i = 0; i < signedJars.size(); i++) {
254            String jarName = signedJars.get(i);
255            try {
256                File file = Support_Resources.getExternalLocalFile(jarDirUrl
257                        + "/" + jarName);
258                jarFile = new JarFile(file, true);
259                boolean foundCerts = false;
260                Enumeration<JarEntry> e = jarFile.entries();
261                while (e.hasMoreElements()) {
262                    JarEntry entry = e.nextElement();
263                    InputStream is = jarFile.getInputStream(entry);
264                    is.skip(100000);
265                    is.close();
266                    Certificate[] certs = entry.getCertificates();
267                    if (certs != null && certs.length > 0) {
268                        foundCerts = true;
269                        break;
270                    }
271                }
272                assertTrue(
273                        "No certificates found during signed jar test for jar \""
274                                + jarName + "\"", foundCerts);
275            } catch (IOException e) {
276                fail("Exception during signed jar test for jar \"" + jarName
277                        + "\": " + e.toString());
278            }
279        }
280    }
281
282    /**
283     * @tests java.util.jar.JarFile#getManifest()
284     */
285    public void testGetManifest() throws Exception {
286        // Test for method java.util.jar.Manifest
287        // java.util.jar.JarFile.getManifest()
288        Support_Resources.copyFile(resources, null, JAR1);
289        JarFile jarFile = new JarFile(new File(resources, JAR1));
290        InputStream is = jarFile.getInputStream(jarFile.getEntry(JAR1_ENTRY1));
291        assertTrue(is.available() > 0);
292        assertNotNull("Error--Manifest not returned", jarFile.getManifest());
293        jarFile.close();
294
295        Support_Resources.copyFile(resources, null, JAR2);
296        jarFile = new JarFile(new File(resources, JAR2));
297        assertNull("Error--should have returned null", jarFile.getManifest());
298        jarFile.close();
299
300        // jarName3 was created using the following test
301        Support_Resources.copyFile(resources, null, JAR3);
302        jarFile = new JarFile(new File(resources, JAR3));
303        assertNotNull("Should find manifest without verifying", jarFile
304                .getManifest());
305        jarFile.close();
306
307        // this is used to create jarName3 used in the previous test
308        Manifest manifest = new Manifest();
309        Attributes attributes = manifest.getMainAttributes();
310        attributes.put(new Attributes.Name("Manifest-Version"), "1.0");
311        ByteArrayOutputStream manOut = new ByteArrayOutputStream();
312        manifest.write(manOut);
313        byte[] manBytes = manOut.toByteArray();
314        File file = new File(Support_PlatformFile.getNewPlatformFile(
315                "hyts_manifest1", ".jar"));
316        JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(file
317                .getAbsolutePath()));
318        ZipEntry entry = new ZipEntry("META-INF/");
319        entry.setSize(0);
320        jarOut.putNextEntry(entry);
321        entry = new ZipEntry(JarFile.MANIFEST_NAME);
322        entry.setSize(manBytes.length);
323        jarOut.putNextEntry(entry);
324        jarOut.write(manBytes);
325        entry = new ZipEntry("myfile");
326        entry.setSize(1);
327        jarOut.putNextEntry(entry);
328        jarOut.write(65);
329        jarOut.close();
330        JarFile jar = new JarFile(file.getAbsolutePath(), false);
331        assertNotNull("Should find manifest without verifying", jar
332                .getManifest());
333        jar.close();
334        file.delete();
335
336        try {
337            Support_Resources.copyFile(resources, null, JAR2);
338            JarFile jF = new JarFile(new File(resources, JAR2));
339            jF.close();
340            jF.getManifest();
341            fail("IllegalStateException expected");
342        } catch (IllegalStateException ise) {
343            // expected;
344        }
345    }
346
347    /**
348     * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry)
349     */
350    public void testGetInputStream() throws Exception {
351        File localFile;
352        byte[] b = new byte[1024];
353        JarFile jf;
354        InputStream is;
355
356        Support_Resources.copyFile(resources, null, JAR1);
357        localFile = new File(resources, JAR1);
358
359        jf = new JarFile(localFile);
360
361        is = jf.getInputStream(new JarEntry("invalid"));
362        assertNull("Got stream for non-existent entry", is);
363
364        is = jf.getInputStream(jf.getEntry(JAR1_ENTRY1));
365        assertTrue("Returned invalid stream", is.available() > 0);
366
367        // try to read class file header
368        is.read(b, 0, 1024);
369        jf.close();
370        assertEquals("Invalid bytes were read", (byte) 0xCA, b[0]);
371        assertEquals("Invalid bytes were read", (byte) 0xFE, b[1]);
372        assertEquals("Invalid bytes were read", (byte) 0xBA, b[2]);
373        assertEquals("Invalid bytes were read", (byte) 0xBE, b[3]);
374    }
375
376    /**
377     * Signed file is verified by default.
378     *
379     * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry)
380     */
381    public void testInputStreamOperations() throws Exception {
382        Support_Resources.copyFile(resources, null, JAR4);
383        File signedFile = new File(resources, JAR4);
384
385        JarFile jar = new JarFile(signedFile);
386        JarEntry entry = new JarEntry(JAR4_SIGNED_ENTRY);
387        InputStream in = jar.getInputStream(entry);
388        in.read();
389
390        // RI verifies only entries which appear via getJarEntry method
391        jar = new JarFile(signedFile);
392        entry = jar.getJarEntry(JAR4_SIGNED_ENTRY);
393        in = jar.getInputStream(entry);
394        readExactly(in, (int) entry.getSize() - 1);
395        assertNull(entry.getCertificates());
396        in.read();
397        assertNotNull(entry.getCertificates());
398        assertEquals(-1, in.read());
399
400        jar = new JarFile(signedFile);
401        entry = jar.getJarEntry(JAR4_SIGNED_ENTRY);
402        entry.setSize(entry.getSize() - 1);
403        in = jar.getInputStream(entry);
404        readExactly(in, (int) entry.getSize() - 1);
405        assertNull(entry.getCertificates());
406        try {
407            in.read();
408            fail("SecurityException expected");
409        } catch (SecurityException e) {
410            // desired
411        }
412        assertEquals(-1, in.read());
413    }
414
415    /**
416     * Performs as many read() calls as necessary to read {@code numBytes} from
417     * the stream. Should the stream exhaust early, this method will fail.
418     */
419    private void readExactly(InputStream in, int numBytes) throws IOException {
420        byte[] buffer = new byte[1024];
421        while (numBytes > 0) {
422            int read = in.read(buffer, 0, Math.min(numBytes, 1024));
423            assertTrue(read != -1);
424            numBytes -= read;
425        }
426    }
427
428    /*
429     * The jar created by 1.4 which does not provide a
430     * algorithm-Digest-Manifest-Main-Attributes entry in .SF file.
431     */
432    public void testJar14() throws IOException {
433        String modifiedJarName = "Created_by_1_4.jar";
434        Support_Resources.copyFile(resources, null, modifiedJarName);
435        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
436                true);
437        Enumeration<JarEntry> entries = jarFile.entries();
438        while (entries.hasMoreElements()) {
439            ZipEntry zipEntry = entries.nextElement();
440            jarFile.getInputStream(zipEntry);
441        }
442    }
443
444    /**
445     * The jar is intact, then everything is all right.
446     */
447    public void testJarVerification() throws IOException {
448        Support_Resources.copyFile(resources, null, JAR5);
449        JarFile jarFile = new JarFile(new File(resources, JAR5), true);
450        Enumeration<JarEntry> entries = jarFile.entries();
451        while (entries.hasMoreElements()) {
452            ZipEntry zipEntry = entries.nextElement();
453            jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE);
454        }
455    }
456
457    /**
458     * The jar is intact, but the entry object is modified.
459     */
460    public void testJarVerificationModifiedEntry() throws IOException {
461        Support_Resources.copyFile(resources, null, JAR5);
462        File f = new File(resources, JAR5);
463
464        JarFile jarFile = new JarFile(f);
465        ZipEntry zipEntry = jarFile.getJarEntry(JAR5_SIGNED_ENTRY);
466        zipEntry.setSize(zipEntry.getSize() + 1);
467        jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE);
468
469        jarFile = new JarFile(f);
470        zipEntry = jarFile.getJarEntry(JAR5_SIGNED_ENTRY);
471        zipEntry.setSize(zipEntry.getSize() - 1);
472        try {
473            //jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE);
474            jarFile.getInputStream(zipEntry).read(new byte[5000], 0, 5000);
475            fail("SecurityException expected");
476        } catch (SecurityException e) {
477            // desired
478        }
479    }
480
481    /*
482     * If another entry is inserted into Manifest, no security exception will be
483     * thrown out.
484     */
485    public void testJarFileInsertEntryInManifestJar() throws IOException {
486        String modifiedJarName = "Inserted_Entry_Manifest.jar";
487        Support_Resources.copyFile(resources, null, modifiedJarName);
488        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
489                true);
490        Enumeration<JarEntry> entries = jarFile.entries();
491        int count = 0;
492        while (entries.hasMoreElements()) {
493
494            ZipEntry zipEntry = entries.nextElement();
495            jarFile.getInputStream(zipEntry);
496            count++;
497        }
498        assertEquals(5, count);
499    }
500
501    /*
502     * If another entry is inserted into Manifest, no security exception will be
503     * thrown out.
504     */
505    public void testInsertedEntryManifestWithDigestCode() throws IOException {
506        String modifiedJarName = "Inserted_Entry_Manifest_with_DigestCode.jar";
507        Support_Resources.copyFile(resources, null, modifiedJarName);
508        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
509                true);
510        Enumeration<JarEntry> entries = jarFile.entries();
511        int count = 0;
512        while (entries.hasMoreElements()) {
513            ZipEntry zipEntry = entries.nextElement();
514            jarFile.getInputStream(zipEntry);
515            count++;
516        }
517        assertEquals(5, count);
518    }
519
520    /*
521     * The content of Test.class is modified, jarFile.getInputStream will not
522     * throw security Exception, but it will anytime before the inputStream got
523     * from getInputStream method has been read to end.
524     */
525    public void testJarFileModifiedClass() throws IOException {
526        String modifiedJarName = "Modified_Class.jar";
527        Support_Resources.copyFile(resources, null, modifiedJarName);
528        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
529                true);
530        Enumeration<JarEntry> entries = jarFile.entries();
531        while (entries.hasMoreElements()) {
532            ZipEntry zipEntry = entries.nextElement();
533            jarFile.getInputStream(zipEntry);
534        }
535        /* The content of Test.class has been tampered. */
536        ZipEntry zipEntry = jarFile.getEntry("Test.class");
537        InputStream in = jarFile.getInputStream(zipEntry);
538        byte[] buffer = new byte[1024];
539        try {
540            while (in.available() > 0) {
541                in.read(buffer);
542            }
543            fail("SecurityException expected");
544        } catch (SecurityException e) {
545            // desired
546        }
547    }
548
549    /*
550     * In the Modified.jar, the main attributes of META-INF/MANIFEST.MF is
551     * tampered manually. Hence the RI 5.0 JarFile.getInputStream of any
552     * JarEntry will throw security exception.
553     */
554    public void testJarFileModifiedManifestMainAttributes() throws IOException {
555        String modifiedJarName = "Modified_Manifest_MainAttributes.jar";
556        Support_Resources.copyFile(resources, null, modifiedJarName);
557        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
558                true);
559        Enumeration<JarEntry> entries = jarFile.entries();
560        while (entries.hasMoreElements()) {
561            ZipEntry zipEntry = entries.nextElement();
562            try {
563                jarFile.getInputStream(zipEntry);
564                fail("SecurityException expected");
565            } catch (SecurityException e) {
566                // desired
567            }
568        }
569    }
570
571    /*
572     * It is all right in our original JarFile. If the Entry Attributes, for
573     * example Test.class in our jar, the jarFile.getInputStream will throw
574     * Security Exception.
575     */
576    public void testJarFileModifiedManifestEntryAttributes() throws IOException {
577        String modifiedJarName = "Modified_Manifest_EntryAttributes.jar";
578        Support_Resources.copyFile(resources, null, modifiedJarName);
579        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
580                true);
581        Enumeration<JarEntry> entries = jarFile.entries();
582        while (entries.hasMoreElements()) {
583            ZipEntry zipEntry = entries.nextElement();
584            try {
585                jarFile.getInputStream(zipEntry);
586                fail("should throw Security Exception");
587            } catch (SecurityException e) {
588                // desired
589            }
590        }
591    }
592
593    /*
594     * If the content of the .SA file is modified, no matter what it resides,
595     * JarFile.getInputStream of any JarEntry will throw Security Exception.
596     */
597    public void testJarFileModifiedSfEntryAttributes() throws IOException {
598        String modifiedJarName = "Modified_SF_EntryAttributes.jar";
599        Support_Resources.copyFile(resources, null, modifiedJarName);
600        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
601                true);
602        Enumeration<JarEntry> entries = jarFile.entries();
603        while (entries.hasMoreElements()) {
604            ZipEntry zipEntry = entries.nextElement();
605            try {
606                jarFile.getInputStream(zipEntry);
607                fail("should throw Security Exception");
608            } catch (SecurityException e) {
609                // desired
610            }
611        }
612    }
613
614    /*
615     * @test JarFile.getInputStream()
616     */
617    public void testGetInputStreamLjava_util_jar_JarEntry() throws IOException {
618        Support_Resources.copyFile(resources, null, JAR1);
619        JarFile jf = new JarFile(new File(resources, JAR1));
620        InputStream is = jf.getInputStream(jf.getEntry(JAR1_ENTRY1));
621        assertTrue(is.available() > 0);
622
623        byte[] buffer = new byte[1024];
624        int r = is.read(buffer, 0, 1024);
625        jf.close();
626        is.close();
627
628        StringBuilder sb = new StringBuilder(r);
629        for (int i = 0; i < r; i++) {
630            sb.append((char) (buffer[i] & 0xff));
631        }
632        String contents = sb.toString();
633        assertTrue(contents.indexOf("foo") > 0);
634        assertTrue(contents.indexOf("bar") > 0);
635
636        try {
637            jf.getInputStream(jf.getEntry(JAR1_ENTRY1));
638            fail("should throw IllegalStateException");
639        } catch (IllegalStateException e) {
640            // Expected
641        }
642
643        jf = new JarFile(new File(resources, JAR1));
644        is = jf.getInputStream(new JarEntry("invalid"));
645        assertNull(is);
646        jf.close();
647    }
648
649    public void testJarVerificationEmptyEntry() throws IOException {
650        Support_Resources.copyFile(resources, null, emptyEntryJar);
651        File f = new File(resources, emptyEntryJar);
652
653        JarFile jarFile = new JarFile(f);
654
655        ZipEntry zipEntry = jarFile.getJarEntry(emptyEntry1);
656        int res = jarFile.getInputStream(zipEntry).read(new byte[100], 0, 100);
657        assertEquals("Wrong length of empty jar entry", -1, res);
658
659        zipEntry = jarFile.getJarEntry(emptyEntry2);
660        res = jarFile.getInputStream(zipEntry).read(new byte[100], 0, 100);
661        assertEquals("Wrong length of empty jar entry", -1, res);
662
663        zipEntry = jarFile.getJarEntry(emptyEntry3);
664        res = jarFile.getInputStream(zipEntry).read();
665        assertEquals("Wrong length of empty jar entry", -1, res);
666    }
667
668    // Regression test for HARMONY-6384
669    public void testJarWrittenWithFlush() throws IOException {
670        File f = new File(resources, "hyts_flushed.jar");
671        Support_Resources.copyFile(resources, null, "hyts_flushed.jar");
672
673        // Used to crash with ZipException: Central Directory Entry not found
674        new JarFile(f);
675    }
676}
677