--- old/src/share/classes/com/sun/java/util/jar/pack/PackerImpl.java 2015-03-30 14:06:38.537897800 +0400
+++ new/src/share/classes/com/sun/java/util/jar/pack/PackerImpl.java 2015-03-30 14:06:35.425002500 +0400
@@ -79,18 +79,20 @@
* Takes a JarFile and converts into a pack-stream.
*
* Closes its input but not its output. (Pack200 archives are appendable.)
- * @param in a JarFile
+ *
+ * @param in a JarFile
* @param out an OutputStream
* @exception IOException if an error is encountered.
*/
public synchronized void pack(JarFile in, OutputStream out) throws IOException {
- assert(Utils.currentInstance.get() == null);
- TimeZone tz = (props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE))
- ? null
- : TimeZone.getDefault();
+ assert (Utils.currentInstance.get() == null);
+
+ boolean needUTC = !props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE);
try {
Utils.currentInstance.set(this);
- if (tz != null) TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ if (needUTC) {
+ Utils.changeDefaultTimeZoneToUtc();
+ }
if ("0".equals(props.getProperty(Pack200.Packer.EFFORT))) {
Utils.copyJarFile(in, out);
@@ -99,7 +101,9 @@
}
} finally {
Utils.currentInstance.set(null);
- if (tz != null) TimeZone.setDefault(tz);
+ if (needUTC) {
+ Utils.restoreDefaultTimeZone();
+ }
in.close();
}
}
@@ -119,12 +123,13 @@
* @exception IOException if an error is encountered.
*/
public synchronized void pack(JarInputStream in, OutputStream out) throws IOException {
- assert(Utils.currentInstance.get() == null);
- TimeZone tz = (props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)) ? null :
- TimeZone.getDefault();
+ assert (Utils.currentInstance.get() == null);
+ boolean needUTC = !props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE);
try {
Utils.currentInstance.set(this);
- if (tz != null) TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ if (needUTC) {
+ Utils.changeDefaultTimeZoneToUtc();
+ }
if ("0".equals(props.getProperty(Pack200.Packer.EFFORT))) {
Utils.copyJarFile(in, out);
} else {
@@ -132,10 +137,13 @@
}
} finally {
Utils.currentInstance.set(null);
- if (tz != null) TimeZone.setDefault(tz);
+ if (needUTC) {
+ Utils.restoreDefaultTimeZone();
+ }
in.close();
}
}
+
/**
* Register a listener for changes to options.
* @param listener An object to be invoked when a property is changed.
--- old/src/share/classes/com/sun/java/util/jar/pack/UnpackerImpl.java 2015-03-30 14:06:56.136632500 +0400
+++ new/src/share/classes/com/sun/java/util/jar/pack/UnpackerImpl.java 2015-03-30 14:06:52.957228800 +0400
@@ -96,13 +96,15 @@
//Driver routines
// The unpack worker...
+
/**
* Takes a packed-stream InputStream, and writes to a JarOutputStream. Internally
* the entire buffer must be read, it may be more efficient to read the packed-stream
* to a file and pass the File object, in the alternate method described below.
*
* Closes its input but not its output. (The output can accumulate more elements.)
- * @param in an InputStream.
+ *
+ * @param in an InputStream.
* @param out a JarOutputStream.
* @exception IOException if an error is encountered.
*/
@@ -113,19 +115,19 @@
if (out == null) {
throw new NullPointerException("null output");
}
- assert(Utils.currentInstance.get() == null);
- TimeZone tz = (props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE))
- ? null
- : TimeZone.getDefault();
-
+ assert (Utils.currentInstance.get() == null);
+ boolean needUTC = !props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE);
try {
Utils.currentInstance.set(this);
- if (tz != null) TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ if (needUTC) {
+ Utils.changeDefaultTimeZoneToUtc();
+ }
final int verbose = props.getInteger(Utils.DEBUG_VERBOSE);
BufferedInputStream in0 = new BufferedInputStream(in);
if (Utils.isJarMagic(Utils.readMagic(in0))) {
- if (verbose > 0)
+ if (verbose > 0) {
Utils.log.info("Copying unpacked JAR file...");
+ }
Utils.copyJarFile(new JarInputStream(in0), out);
} else if (props.getBoolean(Utils.DEBUG_DISABLE_NATIVE)) {
(new DoUnpack()).run(in0, out);
@@ -144,7 +146,9 @@
} finally {
_nunp = null;
Utils.currentInstance.set(null);
- if (tz != null) TimeZone.setDefault(tz);
+ if (needUTC) {
+ Utils.restoreDefaultTimeZone();
+ }
}
}
@@ -152,7 +156,8 @@
* Takes an input File containing the pack file, and generates a JarOutputStream.
*
* Does not close its output. (The output can accumulate more elements.)
- * @param in a File.
+ *
+ * @param in a File.
* @param out a JarOutputStream.
* @exception IOException if an error is encountered.
*/
--- old/src/share/classes/com/sun/java/util/jar/pack/Utils.java 2015-03-30 14:07:13.034278300 +0400
+++ new/src/share/classes/com/sun/java/util/jar/pack/Utils.java 2015-03-30 14:07:10.170914700 +0400
@@ -34,6 +34,7 @@
import java.io.OutputStream;
import java.util.Collections;
import java.util.Date;
+import java.util.TimeZone;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
@@ -133,6 +134,9 @@
// to the engine code, especially the native code.
static final ThreadLocal currentInstance = new ThreadLocal<>();
+ private static TimeZone tz;
+ private static int workingPackerCount = 0;
+
// convenience method to access the TL globals
static TLGlobals getTLGlobals() {
return currentInstance.get();
@@ -203,6 +207,22 @@
}
}
+ static synchronized void changeDefaultTimeZoneToUtc() {
+ if (workingPackerCount++ == 0) {
+ // only first thread saves default TZ
+ tz = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ }
+ }
+
+ static synchronized void restoreDefaultTimeZone() {
+ if (--workingPackerCount == 0) {
+ // reset timezone when all the packer/unpacker instances have terminated
+ if (tz != null) TimeZone.setDefault(tz);
+ tz = null;
+ }
+ }
+
static final Pack200Logger log
= new Pack200Logger("java.util.jar.Pack200");
--- /dev/null 2015-03-30 14:07:28.000000000 +0400
+++ new/test/tools/pack200/DefaultTimeZoneTest.java 2015-03-30 14:07:24.250202500 +0400
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.TimeZone;
+import java.util.concurrent.CountDownLatch;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Pack200;
+
+/*
+ * @test
+ * @bug 8066985
+ * @summary multithreading packing/unpacking files can result in Timezone set to UTC
+ * @compile -XDignore.symbol.file Utils.java DefaultTimeZoneTest.java
+ * @run main/othervm DefaultTimeZoneTest
+ * @author mcherkas
+ */
+
+
+public class DefaultTimeZoneTest {
+
+ private static final TimeZone tz = TimeZone.getTimeZone("Europe/Moscow");
+ private static final int INIT_THREAD_COUNT = Math.min(4, Runtime.getRuntime().availableProcessors());
+ private static final int MAX_THREAD_COUNT = 4 * INIT_THREAD_COUNT;
+ private static final long MINUTE = 60 * 1000;
+
+ private static class NoOpOutputStream extends OutputStream {
+ @Override
+ public void write(int b) throws IOException {
+ // no op
+ }
+ }
+
+ static class PackAction implements Runnable {
+ private Pack200.Packer packer = Pack200.newPacker();
+ private JarFile jarFile;
+
+ PackAction() throws IOException {
+ jarFile = new JarFile(new File("golden.jar"));
+ }
+
+ @Override
+ public void run() {
+ try {
+ packer.pack(jarFile, new NoOpOutputStream());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ static class UnpackAction implements Runnable {
+ private Pack200.Unpacker unpacker = Pack200.newUnpacker();
+ private JarOutputStream jos;
+ private File packedJar = new File("golden.pack");
+
+ UnpackAction() throws IOException {
+ jos = new JarOutputStream(new NoOpOutputStream());
+ }
+
+ @Override
+ public void run() {
+ try {
+ unpacker.unpack(packedJar, jos);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ try {
+ jos.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ };
+
+ public static void test(final Class extends Runnable> runnableClass) throws InterruptedException {
+ for (int i = INIT_THREAD_COUNT; i <= MAX_THREAD_COUNT; i*=2) {
+ final CountDownLatch startLatch = new CountDownLatch(i);
+ final CountDownLatch doneLatch = new CountDownLatch(i);
+ for (int j = 0; j < i; j++) {
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ Runnable r = runnableClass.newInstance();
+ startLatch.countDown();
+ startLatch.await();
+ r.run();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ doneLatch.countDown();
+ }
+ }
+ }.start();
+ }
+ doneLatch.await();
+
+ if(!TimeZone.getDefault().equals(tz)) {
+ throw new RuntimeException("FAIL: default time zone was changed");
+ }
+ }
+ }
+
+ public static void main(String args[]) throws IOException, InterruptedException {
+ TimeZone.setDefault(tz);
+
+ // make a local copy of our test file
+ File srcFile = Utils.locateJar("golden.jar");
+ final File goldenFile = new File("golden.jar");
+ Utils.copyFile(srcFile, goldenFile);
+
+ // created packed file
+ final JarFile goldenJarFile = new JarFile(goldenFile);
+ final File packFile = new File("golden.pack");
+ Utils.pack(goldenJarFile, packFile);
+
+
+ long startTime = System.currentTimeMillis();
+ while(System.currentTimeMillis() - startTime < MINUTE) {
+ // test packer
+ test(PackAction.class);
+
+ // test unpacker
+ test(UnpackAction.class);
+ }
+
+ Utils.cleanup();
+ }
+}