--- a/jdk/make/common/Release.gmk Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/make/common/Release.gmk Fri Dec 28 18:36:41 2012 -0800
@@ -366,6 +366,7 @@
com/sun/source \
com/sun/tools/classfile \
com/sun/tools/doclets \
+ com/sun/tools/doclint \
com/sun/tools/example/debug/expr \
com/sun/tools/example/debug/tty \
com/sun/tools/extcheck \
--- a/jdk/make/common/internal/Defs-langtools.gmk Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/make/common/internal/Defs-langtools.gmk Fri Dec 28 18:36:41 2012 -0800
@@ -35,6 +35,7 @@
com/sun/source \
com/sun/tools/classfile \
com/sun/tools/doclets \
+ com/sun/tools/doclint \
com/sun/tools/javac \
com/sun/tools/javadoc \
com/sun/tools/javah \
--- a/jdk/make/docs/NON_CORE_PKGS.gmk Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/make/docs/NON_CORE_PKGS.gmk Fri Dec 28 18:36:41 2012 -0800
@@ -76,7 +76,8 @@
JCONSOLE_PKGS = com.sun.tools.jconsole
-TREEAPI_PKGS = com.sun.source.tree \
+TREEAPI_PKGS = com.sunsource.doctree \
+ com.sun.source.tree \
com.sun.source.util
SMARTCARDIO_PKGS = javax.smartcardio
--- a/jdk/make/java/java/FILES_java.gmk Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/make/java/java/FILES_java.gmk Fri Dec 28 18:36:41 2012 -0800
@@ -294,6 +294,7 @@
java/util/IdentityHashMap.java \
java/util/EnumMap.java \
java/util/Arrays.java \
+ java/util/ArraysParallelSortHelpers.java \
java/util/DualPivotQuicksort.java \
java/util/TimSort.java \
java/util/ComparableTimSort.java \
@@ -322,6 +323,7 @@
java/util/concurrent/CopyOnWriteArrayList.java \
java/util/concurrent/CopyOnWriteArraySet.java \
java/util/concurrent/CountDownLatch.java \
+ java/util/concurrent/CountedCompleter.java \
java/util/concurrent/CyclicBarrier.java \
java/util/concurrent/DelayQueue.java \
java/util/concurrent/Delayed.java \
--- a/jdk/make/jdk/Makefile Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/make/jdk/Makefile Fri Dec 28 18:36:41 2012 -0800
@@ -1,5 +1,5 @@
#
-# Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 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
@@ -23,16 +23,18 @@
# questions.
#
-#
-# Makefile for building all of java
-#
-
BUILDDIR = ..
+PACKAGE = jdk
PRODUCT = jdk
+JAVAC_LINT_OPTIONS=-Xlint:all
include $(BUILDDIR)/common/Defs.gmk
-SUBDIRS = asm
-include $(BUILDDIR)/common/Subdirs.gmk
+#
+# Files to compile
+#
+AUTO_FILES_JAVA_DIRS = jdk
-all build clean clobber::
- $(SUBDIRS-loop)
+#
+# Rules
+#
+include $(BUILDDIR)/common/Classes.gmk
--- a/jdk/make/jdk/asm/Makefile Fri Dec 28 18:30:56 2012 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-#
-# Copyright (c) 1995, 2012, 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. Oracle designates this
-# particular file as subject to the "Classpath" exception as provided
-# by Oracle in the LICENSE file that accompanied this code.
-#
-# 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.
-#
-
-BUILDDIR = ../..
-PACKAGE = jdk.internal.org.objectweb.asm
-PRODUCT = jdk
-JAVAC_LINT_OPTIONS=-Xlint:all
-include $(BUILDDIR)/common/Defs.gmk
-
-#
-# Files to compile
-#
-AUTO_FILES_JAVA_DIRS = jdk/internal/org/objectweb/asm
-
-#
-# Rules
-#
-include $(BUILDDIR)/common/Classes.gmk
--- a/jdk/make/netbeans/jmx/build.properties Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/make/netbeans/jmx/build.properties Fri Dec 28 18:36:41 2012 -0800
@@ -38,6 +38,7 @@
com/sun/jmx/snmp/
jtreg.tests=\
+ com/sun/jmx/ \
com/sun/management/ \
java/lang/management/ \
javax/management/
--- a/jdk/makefiles/CreateJars.gmk Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/makefiles/CreateJars.gmk Fri Dec 28 18:36:41 2012 -0800
@@ -729,6 +729,7 @@
com/sun/source \
com/sun/tools/classfile \
com/sun/tools/doclets \
+ com/sun/tools/doclint \
com/sun/tools/example/debug/expr \
com/sun/tools/example/debug/tty \
com/sun/tools/extcheck \
--- a/jdk/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java Fri Dec 28 18:36:41 2012 -0800
@@ -115,6 +115,7 @@
// restarted is failed by another thread
throw ioe;
}
+ return;
} else {
state = RE_CONNECTING;
lock.notifyAll();
@@ -195,7 +196,7 @@
if (e instanceof IOException &&
!(e instanceof InterruptedIOException)) {
try {
- restart((IOException)e);
+ gotIOException((IOException)e);
} catch (Exception ee) {
logger.warning("Checker-run",
"Failed to check connection: "+ e);
--- a/jdk/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java Fri Dec 28 18:36:41 2012 -0800
@@ -51,6 +51,7 @@
import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;
+import java.rmi.UnmarshalException;
public abstract class ClientNotifForwarder {
@@ -594,10 +595,7 @@
}
return nr;
- } catch (ClassNotFoundException e) {
- logger.trace("NotifFetcher.fetchNotifs", e);
- return fetchOneNotif();
- } catch (NotSerializableException e) {
+ } catch (ClassNotFoundException | NotSerializableException | UnmarshalException e) {
logger.trace("NotifFetcher.fetchNotifs", e);
return fetchOneNotif();
} catch (IOException ioe) {
@@ -619,17 +617,18 @@
timeout. This allows us to skip sequence numbers for
notifications that don't match our filters. Then we ask
for one notification. If that produces a
- ClassNotFoundException or a NotSerializableException, we
- increase our sequence number and ask again. Eventually we
- will either get a successful notification, or a return with
- 0 notifications. In either case we can return a
+ ClassNotFoundException, NotSerializableException or
+ UnmarshalException, we increase our sequence number and ask again.
+ Eventually we will either get a successful notification, or a
+ return with 0 notifications. In either case we can return a
NotificationResult. This algorithm works (albeit less
well) even if the server implementation doesn't optimize a
request for 0 notifications to skip sequence numbers for
notifications that don't match our filters.
- If we had at least one ClassNotFoundException, then we
- must emit a JMXConnectionNotification.LOST_NOTIFS.
+ If we had at least one
+ ClassNotFoundException/NotSerializableException/UnmarshalException,
+ then we must emit a JMXConnectionNotification.LOST_NOTIFS.
*/
private NotificationResult fetchOneNotif() {
ClientNotifForwarder cnf = ClientNotifForwarder.this;
@@ -668,23 +667,20 @@
try {
// 1 notif to skip possible missing class
result = cnf.fetchNotifs(startSequenceNumber, 1, 0L);
+ } catch (ClassNotFoundException | NotSerializableException | UnmarshalException e) {
+ logger.warning("NotifFetcher.fetchOneNotif",
+ "Failed to deserialize a notification: "+e.toString());
+ if (logger.traceOn()) {
+ logger.trace("NotifFetcher.fetchOneNotif",
+ "Failed to deserialize a notification.", e);
+ }
+
+ notFoundCount++;
+ startSequenceNumber++;
} catch (Exception e) {
- if (e instanceof ClassNotFoundException
- || e instanceof NotSerializableException) {
- logger.warning("NotifFetcher.fetchOneNotif",
- "Failed to deserialize a notification: "+e.toString());
- if (logger.traceOn()) {
- logger.trace("NotifFetcher.fetchOneNotif",
- "Failed to deserialize a notification.", e);
- }
-
- notFoundCount++;
- startSequenceNumber++;
- } else {
- if (!shouldStop())
- logger.trace("NotifFetcher.fetchOneNotif", e);
- return null;
- }
+ if (!shouldStop())
+ logger.trace("NotifFetcher.fetchOneNotif", e);
+ return null;
}
}
@@ -692,7 +688,7 @@
final String msg =
"Dropped " + notFoundCount + " notification" +
(notFoundCount == 1 ? "" : "s") +
- " because classes were missing locally";
+ " because classes were missing locally or incompatible";
lostNotifs(msg, notFoundCount);
// Even if result.getEarliestSequenceNumber() is now greater than
// it was initially, meaning some notifs have been dropped
--- a/jdk/src/share/classes/com/sun/jmx/remote/internal/IIOPHelper.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/com/sun/jmx/remote/internal/IIOPHelper.java Fri Dec 28 18:36:41 2012 -0800
@@ -26,13 +26,8 @@
package com.sun.jmx.remote.internal;
import java.util.Properties;
+import java.io.IOException;
import java.rmi.Remote;
-import java.rmi.RemoteException;
-import java.rmi.NoSuchObjectException;
-
-import java.util.Properties;
-import java.rmi.Remote;
-import java.rmi.RemoteException;
import java.rmi.NoSuchObjectException;
import java.security.AccessController;
@@ -115,9 +110,10 @@
* Connects the Stub to the given ORB.
*/
public static void connect(Object stub, Object orb)
- throws RemoteException
+ throws IOException
{
- ensureAvailable();
+ if (proxy == null)
+ throw new IOException("Connection to ORB failed, RMI/IIOP not available");
proxy.connect(stub, orb);
}
@@ -125,15 +121,17 @@
* Returns true if the given object is an ORB.
*/
public static boolean isOrb(Object obj) {
- ensureAvailable();
- return proxy.isOrb(obj);
+ return (proxy == null) ? false : proxy.isOrb(obj);
}
/**
* Creates, and returns, a new ORB instance.
*/
- public static Object createOrb(String[] args, Properties props) {
- ensureAvailable();
+ public static Object createOrb(String[] args, Properties props)
+ throws IOException
+ {
+ if (proxy == null)
+ throw new IOException("ORB initialization failed, RMI/IIOP not available");
return proxy.createOrb(args, props);
}
@@ -166,24 +164,27 @@
/**
* Makes a server object ready to receive remote calls
*/
- public static void exportObject(Remote obj) throws RemoteException {
- ensureAvailable();
+ public static void exportObject(Remote obj) throws IOException {
+ if (proxy == null)
+ throw new IOException("RMI object cannot be exported, RMI/IIOP not available");
proxy.exportObject(obj);
}
/**
* Deregisters a server object from the runtime.
*/
- public static void unexportObject(Remote obj) throws NoSuchObjectException {
- ensureAvailable();
+ public static void unexportObject(Remote obj) throws IOException {
+ if (proxy == null)
+ throw new NoSuchObjectException("Object not exported");
proxy.unexportObject(obj);
}
/**
* Returns a stub for the given server object.
*/
- public static Remote toStub(Remote obj) throws NoSuchObjectException {
- ensureAvailable();
+ public static Remote toStub(Remote obj) throws IOException {
+ if (proxy == null)
+ throw new NoSuchObjectException("Object not exported");
return proxy.toStub(obj);
}
}
--- a/jdk/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java Fri Dec 28 18:36:41 2012 -0800
@@ -25,16 +25,15 @@
package com.sun.jmx.remote.internal;
-import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.remote.security.NotificationAccessController;
import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -291,13 +290,18 @@
// so that we can know too, and remove the corresponding entry from the listenerMap.
// See 6957378.
private void snoopOnUnregister(NotificationResult nr) {
- Set<IdAndFilter> delegateSet = listenerMap.get(MBeanServerDelegate.DELEGATE_NAME);
- if (delegateSet == null || delegateSet.isEmpty()) {
- return;
+ List<IdAndFilter> copy = null;
+ synchronized (listenerMap) {
+ Set<IdAndFilter> delegateSet = listenerMap.get(MBeanServerDelegate.DELEGATE_NAME);
+ if (delegateSet == null || delegateSet.isEmpty()) {
+ return;
+ }
+ copy = new ArrayList<>(delegateSet);
}
+
for (TargetedNotification tn : nr.getTargetedNotifications()) {
Integer id = tn.getListenerID();
- for (IdAndFilter idaf : delegateSet) {
+ for (IdAndFilter idaf : copy) {
if (idaf.id == id) {
// This is a notification from the MBeanServerDelegate.
Notification n = tn.getNotification();
--- a/jdk/src/share/classes/com/sun/security/auth/login/ConfigFile.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/com/sun/security/auth/login/ConfigFile.java Fri Dec 28 18:36:41 2012 -0800
@@ -25,41 +25,39 @@
package com.sun.security.auth.login;
-import javax.security.auth.AuthPermission;
import javax.security.auth.login.AppConfigurationEntry;
-import java.io.*;
-import java.util.*;
+import javax.security.auth.login.Configuration;
import java.net.URI;
-import java.net.URL;
-import java.net.MalformedURLException;
-import java.text.MessageFormat;
-import sun.security.util.Debug;
-import sun.security.util.ResourcesMgr;
-import sun.security.util.PropertyExpander;
+
+// NOTE: As of JDK 8, this class instantiates
+// sun.security.provider.ConfigSpiFile and forwards all methods to that
+// implementation. All implementation fixes and enhancements should be made to
+// sun.security.provider.ConfigSpiFile and not this class.
+// See JDK-8005117 for more information.
/**
* This class represents a default implementation for
- * <code>javax.security.auth.login.Configuration</code>.
+ * {@code javax.security.auth.login.Configuration}.
*
* <p> This object stores the runtime login configuration representation,
* and is the amalgamation of multiple static login
* configurations that resides in files.
* The algorithm for locating the login configuration file(s) and reading their
- * information into this <code>Configuration</code> object is:
+ * information into this {@code Configuration} object is:
*
* <ol>
* <li>
* Loop through the security properties,
* <i>login.config.url.1</i>, <i>login.config.url.2</i>, ...,
* <i>login.config.url.X</i>.
- * Each property value specifies a <code>URL</code> pointing to a
+ * Each property value specifies a {@code URL} pointing to a
* login configuration file to be loaded. Read in and load
* each configuration.
*
* <li>
- * The <code>java.lang.System</code> property
+ * The {@code java.lang.System} property
* <i>java.security.auth.login.config</i>
- * may also be set to a <code>URL</code> pointing to another
+ * may also be set to a {@code URL} pointing to another
* login configuration file
* (which is the case when a user uses the -D switch at runtime).
* If this property is defined, and its use is allowed by the
@@ -80,593 +78,63 @@
*
* <p> The configuration syntax supported by this implementation
* is exactly that syntax specified in the
- * <code>javax.security.auth.login.Configuration</code> class.
+ * {@code javax.security.auth.login.Configuration} class.
*
* @see javax.security.auth.login.LoginContext
* @see java.security.Security security properties
*/
-public class ConfigFile extends javax.security.auth.login.Configuration {
+public class ConfigFile extends Configuration {
- private StreamTokenizer st;
- private int lookahead;
- private int linenum;
- private HashMap<String, LinkedList<AppConfigurationEntry>> configuration;
- private boolean expandProp = true;
- private URL url;
-
- private static Debug debugConfig = Debug.getInstance("configfile");
- private static Debug debugParser = Debug.getInstance("configparser");
+ private sun.security.provider.ConfigSpiFile configFile;
/**
- * Create a new <code>Configuration</code> object.
+ * Create a new {@code Configuration} object.
+ *
+ * @throws SecurityException if the {@code Configuration} can not be
+ * initialized
*/
public ConfigFile() {
- try {
- init(url);
- } catch (IOException ioe) {
- throw (SecurityException)
- new SecurityException(ioe.getMessage()).initCause(ioe);
- }
- }
-
- /**
- * Create a new <code>Configuration</code> object from the specified URI.
- *
- * @param uri Create a new Configuration object from this URI.
- */
- public ConfigFile(URI uri) {
- // only load config from the specified URI
- try {
- url = uri.toURL();
- init(url);
- } catch (MalformedURLException mue) {
- throw (SecurityException)
- new SecurityException(mue.getMessage()).initCause(mue);
- } catch (IOException ioe) {
- throw (SecurityException)
- new SecurityException(ioe.getMessage()).initCause(ioe);
- }
+ configFile = new sun.security.provider.ConfigSpiFile();
}
/**
- * Read and initialize the entire login Configuration.
- *
- * <p>
+ * Create a new {@code Configuration} object from the specified {@code URI}.
*
- * @exception IOException if the Configuration can not be initialized. <p>
- * @exception SecurityException if the caller does not have permission
- * to initialize the Configuration.
+ * @param uri the {@code URI}
+ * @throws SecurityException if the {@code Configuration} can not be
+ * initialized
+ * @throws NullPointerException if {@code uri} is null
*/
- private void init(URL url) throws IOException {
-
- boolean initialized = false;
- FileReader fr = null;
- String sep = File.separator;
-
- if ("false".equals(System.getProperty("policy.expandProperties"))) {
- expandProp = false;
- }
-
- // new configuration
- HashMap<String, LinkedList<AppConfigurationEntry>> newConfig =
- new HashMap<>();
-
- if (url != null) {
-
- /**
- * If the caller specified a URI via Configuration.getInstance,
- * we only read from that URI
- */
- if (debugConfig != null) {
- debugConfig.println("reading " + url);
- }
- init(url, newConfig);
- configuration = newConfig;
- return;
- }
-
- /**
- * Caller did not specify URI via Configuration.getInstance.
- * Read from URLs listed in the java.security properties file.
- */
-
- String allowSys = java.security.Security.getProperty
- ("policy.allowSystemProperty");
-
- if ("true".equalsIgnoreCase(allowSys)) {
- String extra_config = System.getProperty
- ("java.security.auth.login.config");
- if (extra_config != null) {
- boolean overrideAll = false;
- if (extra_config.startsWith("=")) {
- overrideAll = true;
- extra_config = extra_config.substring(1);
- }
- try {
- extra_config = PropertyExpander.expand(extra_config);
- } catch (PropertyExpander.ExpandException peee) {
- MessageFormat form = new MessageFormat
- (ResourcesMgr.getString
- ("Unable.to.properly.expand.config",
- "sun.security.util.AuthResources"));
- Object[] source = {extra_config};
- throw new IOException(form.format(source));
- }
-
- URL configURL = null;
- try {
- configURL = new URL(extra_config);
- } catch (java.net.MalformedURLException mue) {
- File configFile = new File(extra_config);
- if (configFile.exists()) {
- configURL = configFile.toURI().toURL();
- } else {
- MessageFormat form = new MessageFormat
- (ResourcesMgr.getString
- ("extra.config.No.such.file.or.directory.",
- "sun.security.util.AuthResources"));
- Object[] source = {extra_config};
- throw new IOException(form.format(source));
- }
- }
-
- if (debugConfig != null) {
- debugConfig.println("reading "+configURL);
- }
- init(configURL, newConfig);
- initialized = true;
- if (overrideAll) {
- if (debugConfig != null) {
- debugConfig.println("overriding other policies!");
- }
- configuration = newConfig;
- return;
- }
- }
- }
-
- int n = 1;
- String config_url;
- while ((config_url = java.security.Security.getProperty
- ("login.config.url."+n)) != null) {
- try {
- config_url = PropertyExpander.expand
- (config_url).replace(File.separatorChar, '/');
- if (debugConfig != null) {
- debugConfig.println("\tReading config: " + config_url);
- }
- init(new URL(config_url), newConfig);
- initialized = true;
- } catch (PropertyExpander.ExpandException peee) {
- MessageFormat form = new MessageFormat
- (ResourcesMgr.getString
- ("Unable.to.properly.expand.config",
- "sun.security.util.AuthResources"));
- Object[] source = {config_url};
- throw new IOException(form.format(source));
- }
- n++;
- }
-
- if (initialized == false && n == 1 && config_url == null) {
-
- // get the config from the user's home directory
- if (debugConfig != null) {
- debugConfig.println("\tReading Policy " +
- "from ~/.java.login.config");
- }
- config_url = System.getProperty("user.home");
- String userConfigFile = config_url +
- File.separatorChar + ".java.login.config";
-
- // No longer throws an exception when there's no config file
- // at all. Returns an empty Configuration instead.
- if (new File(userConfigFile).exists()) {
- init(new File(userConfigFile).toURI().toURL(),
- newConfig);
- }
- }
-
- configuration = newConfig;
- }
-
- private void init(URL config,
- HashMap<String, LinkedList<AppConfigurationEntry>> newConfig)
- throws IOException {
-
- InputStreamReader isr = null;
- try {
- isr = new InputStreamReader(getInputStream(config), "UTF-8");
- readConfig(isr, newConfig);
- } catch (FileNotFoundException fnfe) {
- if (debugConfig != null) {
- debugConfig.println(fnfe.toString());
- }
- throw new IOException(ResourcesMgr.getString
- ("Configuration.Error.No.such.file.or.directory",
- "sun.security.util.AuthResources"));
- } finally {
- if (isr != null) {
- isr.close();
- }
- }
- }
-
- /**
- * Retrieve an entry from the Configuration using an application name
- * as an index.
- *
- * <p>
- *
- * @param applicationName the name used to index the Configuration.
- * @return an array of AppConfigurationEntries which correspond to
- * the stacked configuration of LoginModules for this
- * application, or null if this application has no configured
- * LoginModules.
- */
- public AppConfigurationEntry[] getAppConfigurationEntry
- (String applicationName) {
-
- LinkedList<AppConfigurationEntry> list = null;
- synchronized (configuration) {
- list = configuration.get(applicationName);
- }
-
- if (list == null || list.size() == 0)
- return null;
-
- AppConfigurationEntry[] entries =
- new AppConfigurationEntry[list.size()];
- Iterator<AppConfigurationEntry> iterator = list.iterator();
- for (int i = 0; iterator.hasNext(); i++) {
- AppConfigurationEntry e = iterator.next();
- entries[i] = new AppConfigurationEntry(e.getLoginModuleName(),
- e.getControlFlag(),
- e.getOptions());
- }
- return entries;
+ public ConfigFile(URI uri) {
+ configFile = new sun.security.provider.ConfigSpiFile(uri);
}
/**
- * Refresh and reload the Configuration by re-reading all of the
- * login configurations.
+ * Retrieve an entry from the {@code Configuration} using an application
+ * name as an index.
*
- * <p>
- *
- * @exception SecurityException if the caller does not have permission
- * to refresh the Configuration.
+ * @param applicationName the name used to index the {@code Configuration}
+ * @return an array of {@code AppConfigurationEntry} which correspond to
+ * the stacked configuration of {@code LoginModule}s for this
+ * application, or null if this application has no configured
+ * {@code LoginModule}s.
*/
- public synchronized void refresh() {
-
- java.lang.SecurityManager sm = System.getSecurityManager();
- if (sm != null)
- sm.checkPermission(new AuthPermission("refreshLoginConfiguration"));
-
- java.security.AccessController.doPrivileged
- (new java.security.PrivilegedAction<Void>() {
- public Void run() {
- try {
- init(url);
- } catch (java.io.IOException ioe) {
- throw (SecurityException) new SecurityException
- (ioe.getLocalizedMessage()).initCause(ioe);
- }
- return null;
- }
- });
- }
-
- private void readConfig(Reader reader,
- HashMap<String, LinkedList<AppConfigurationEntry>> newConfig)
- throws IOException {
-
- int linenum = 1;
-
- if (!(reader instanceof BufferedReader))
- reader = new BufferedReader(reader);
-
- st = new StreamTokenizer(reader);
- st.quoteChar('"');
- st.wordChars('$', '$');
- st.wordChars('_', '_');
- st.wordChars('-', '-');
- st.lowerCaseMode(false);
- st.slashSlashComments(true);
- st.slashStarComments(true);
- st.eolIsSignificant(true);
-
- lookahead = nextToken();
- while (lookahead != StreamTokenizer.TT_EOF) {
- parseLoginEntry(newConfig);
- }
- }
-
- private void parseLoginEntry(
- HashMap<String, LinkedList<AppConfigurationEntry>> newConfig)
- throws IOException {
-
- String appName;
- String moduleClass;
- String sflag;
- AppConfigurationEntry.LoginModuleControlFlag controlFlag;
- LinkedList<AppConfigurationEntry> configEntries = new LinkedList<>();
-
- // application name
- appName = st.sval;
- lookahead = nextToken();
-
- if (debugParser != null) {
- debugParser.println("\tReading next config entry: " + appName);
- }
-
- match("{");
+ @Override
+ public AppConfigurationEntry[] getAppConfigurationEntry
+ (String applicationName) {
- // get the modules
- while (peek("}") == false) {
- // get the module class name
- moduleClass = match("module class name");
-
- // controlFlag (required, optional, etc)
- sflag = match("controlFlag");
- if (sflag.equalsIgnoreCase("REQUIRED"))
- controlFlag =
- AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
- else if (sflag.equalsIgnoreCase("REQUISITE"))
- controlFlag =
- AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
- else if (sflag.equalsIgnoreCase("SUFFICIENT"))
- controlFlag =
- AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
- else if (sflag.equalsIgnoreCase("OPTIONAL"))
- controlFlag =
- AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
- else {
- MessageFormat form = new MessageFormat(ResourcesMgr.getString
- ("Configuration.Error.Invalid.control.flag.flag",
- "sun.security.util.AuthResources"));
- Object[] source = {sflag};
- throw new IOException(form.format(source));
- }
-
- // get the args
- HashMap<String, String> options = new HashMap<>();
- String key;
- String value;
- while (peek(";") == false) {
- key = match("option key");
- match("=");
- try {
- value = expand(match("option value"));
- } catch (PropertyExpander.ExpandException peee) {
- throw new IOException(peee.getLocalizedMessage());
- }
- options.put(key, value);
- }
-
- lookahead = nextToken();
-
- // create the new element
- if (debugParser != null) {
- debugParser.println("\t\t" + moduleClass + ", " + sflag);
- java.util.Iterator<String> i = options.keySet().iterator();
- while (i.hasNext()) {
- key = i.next();
- debugParser.println("\t\t\t" +
- key +
- "=" +
- options.get(key));
- }
- }
- AppConfigurationEntry entry = new AppConfigurationEntry
- (moduleClass,
- controlFlag,
- options);
- configEntries.add(entry);
- }
-
- match("}");
- match(";");
-
- // add this configuration entry
- if (newConfig.containsKey(appName)) {
- MessageFormat form = new MessageFormat(ResourcesMgr.getString
- ("Configuration.Error.Can.not.specify.multiple.entries.for.appName",
- "sun.security.util.AuthResources"));
- Object[] source = {appName};
- throw new IOException(form.format(source));
- }
- newConfig.put(appName, configEntries);
+ return configFile.engineGetAppConfigurationEntry(applicationName);
}
- private String match(String expect) throws IOException {
-
- String value = null;
-
- switch(lookahead) {
- case StreamTokenizer.TT_EOF:
-
- MessageFormat form1 = new MessageFormat(ResourcesMgr.getString
- ("Configuration.Error.expected.expect.read.end.of.file.",
- "sun.security.util.AuthResources"));
- Object[] source1 = {expect};
- throw new IOException(form1.format(source1));
-
- case '"':
- case StreamTokenizer.TT_WORD:
-
- if (expect.equalsIgnoreCase("module class name") ||
- expect.equalsIgnoreCase("controlFlag") ||
- expect.equalsIgnoreCase("option key") ||
- expect.equalsIgnoreCase("option value")) {
- value = st.sval;
- lookahead = nextToken();
- } else {
- MessageFormat form = new MessageFormat(ResourcesMgr.getString
- ("Configuration.Error.Line.line.expected.expect.found.value.",
- "sun.security.util.AuthResources"));
- Object[] source = {new Integer(linenum), expect, st.sval};
- throw new IOException(form.format(source));
- }
- break;
-
- case '{':
-
- if (expect.equalsIgnoreCase("{")) {
- lookahead = nextToken();
- } else {
- MessageFormat form = new MessageFormat(ResourcesMgr.getString
- ("Configuration.Error.Line.line.expected.expect.",
- "sun.security.util.AuthResources"));
- Object[] source = {new Integer(linenum), expect, st.sval};
- throw new IOException(form.format(source));
- }
- break;
-
- case ';':
-
- if (expect.equalsIgnoreCase(";")) {
- lookahead = nextToken();
- } else {
- MessageFormat form = new MessageFormat(ResourcesMgr.getString
- ("Configuration.Error.Line.line.expected.expect.",
- "sun.security.util.AuthResources"));
- Object[] source = {new Integer(linenum), expect, st.sval};
- throw new IOException(form.format(source));
- }
- break;
-
- case '}':
-
- if (expect.equalsIgnoreCase("}")) {
- lookahead = nextToken();
- } else {
- MessageFormat form = new MessageFormat(ResourcesMgr.getString
- ("Configuration.Error.Line.line.expected.expect.",
- "sun.security.util.AuthResources"));
- Object[] source = {new Integer(linenum), expect, st.sval};
- throw new IOException(form.format(source));
- }
- break;
-
- case '=':
-
- if (expect.equalsIgnoreCase("=")) {
- lookahead = nextToken();
- } else {
- MessageFormat form = new MessageFormat(ResourcesMgr.getString
- ("Configuration.Error.Line.line.expected.expect.",
- "sun.security.util.AuthResources"));
- Object[] source = {new Integer(linenum), expect, st.sval};
- throw new IOException(form.format(source));
- }
- break;
-
- default:
- MessageFormat form = new MessageFormat(ResourcesMgr.getString
- ("Configuration.Error.Line.line.expected.expect.found.value.",
- "sun.security.util.AuthResources"));
- Object[] source = {new Integer(linenum), expect, st.sval};
- throw new IOException(form.format(source));
- }
- return value;
- }
-
- private boolean peek(String expect) {
- boolean found = false;
-
- switch (lookahead) {
- case ',':
- if (expect.equalsIgnoreCase(","))
- found = true;
- break;
- case ';':
- if (expect.equalsIgnoreCase(";"))
- found = true;
- break;
- case '{':
- if (expect.equalsIgnoreCase("{"))
- found = true;
- break;
- case '}':
- if (expect.equalsIgnoreCase("}"))
- found = true;
- break;
- default:
- }
- return found;
- }
-
- private int nextToken() throws IOException {
- int tok;
- while ((tok = st.nextToken()) == StreamTokenizer.TT_EOL) {
- linenum++;
- }
- return tok;
- }
-
- /*
- * Fast path reading from file urls in order to avoid calling
- * FileURLConnection.connect() which can be quite slow the first time
- * it is called. We really should clean up FileURLConnection so that
- * this is not a problem but in the meantime this fix helps reduce
- * start up time noticeably for the new launcher. -- DAC
+ /**
+ * Refresh and reload the {@code Configuration} by re-reading all of the
+ * login configurations.
+ *
+ * @throws SecurityException if the caller does not have permission
+ * to refresh the {@code Configuration}
*/
- private InputStream getInputStream(URL url) throws IOException {
- if ("file".equalsIgnoreCase(url.getProtocol())) {
- // Compatibility notes:
- //
- // Code changed from
- // String path = url.getFile().replace('/', File.separatorChar);
- // return new FileInputStream(path);
- //
- // The original implementation would search for "/tmp/a%20b"
- // when url is "file:///tmp/a%20b". This is incorrect. The
- // current codes fix this bug and searches for "/tmp/a b".
- // For compatibility reasons, when the file "/tmp/a b" does
- // not exist, the file named "/tmp/a%20b" will be tried.
- //
- // This also means that if both file exists, the behavior of
- // this method is changed, and the current codes choose the
- // correct one.
- try {
- return url.openStream();
- } catch (Exception e) {
- String file = url.getPath();
- if (url.getHost().length() > 0) { // For Windows UNC
- file = "//" + url.getHost() + file;
- }
- if (debugConfig != null) {
- debugConfig.println("cannot read " + url +
- ", try " + file);
- }
- return new FileInputStream(file);
- }
- } else {
- return url.openStream();
- }
- }
-
- private String expand(String value)
- throws PropertyExpander.ExpandException, IOException {
-
- if ("".equals(value)) {
- return value;
- }
-
- if (expandProp) {
-
- String s = PropertyExpander.expand(value);
-
- if (s == null || s.length() == 0) {
- MessageFormat form = new MessageFormat(ResourcesMgr.getString
- ("Configuration.Error.Line.line.system.property.value.expanded.to.empty.value",
- "sun.security.util.AuthResources"));
- Object[] source = {new Integer(linenum), value};
- throw new IOException(form.format(source));
- }
- return s;
- } else {
- return value;
- }
+ @Override
+ public synchronized void refresh() {
+ configFile.engineRefresh();
}
}
--- a/jdk/src/share/classes/java/lang/Class.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/lang/Class.java Fri Dec 28 18:36:41 2012 -0800
@@ -506,6 +506,7 @@
* returns {@code false} otherwise.
* @return {@code true} if and only if this class is a synthetic class as
* defined by the Java Language Specification.
+ * @jls 13.1 The Form of a Binary
* @since 1.5
*/
public boolean isSynthetic() {
--- a/jdk/src/share/classes/java/lang/reflect/Constructor.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/lang/reflect/Constructor.java Fri Dec 28 18:36:41 2012 -0800
@@ -66,6 +66,8 @@
private transient ConstructorRepository genericInfo;
private byte[] annotations;
private byte[] parameterAnnotations;
+ // This is set by the vm at Constructor creation
+ private byte[] typeAnnotations;
// Generics infrastructure
// Accessor for factory
@@ -138,6 +140,8 @@
res.root = this;
// Might as well eagerly propagate this if already present
res.constructorAccessor = constructorAccessor;
+
+ res.typeAnnotations = typeAnnotations;
return res;
}
@@ -407,6 +411,7 @@
/**
* {@inheritDoc}
+ * @jls 13.1 The Form of a Binary
* @since 1.5
*/
@Override
--- a/jdk/src/share/classes/java/lang/reflect/Executable.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/lang/reflect/Executable.java Fri Dec 28 18:36:41 2012 -0800
@@ -324,6 +324,7 @@
* @return true if and only if this executable is a synthetic
* construct as defined by
* <cite>The Java™ Language Specification</cite>.
+ * @jls 13.1 The Form of a Binary
*/
public boolean isSynthetic() {
return Modifier.isSynthetic(getModifiers());
--- a/jdk/src/share/classes/java/lang/reflect/Field.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/lang/reflect/Field.java Fri Dec 28 18:36:41 2012 -0800
@@ -80,6 +80,8 @@
// currently only two levels deep (i.e., one root Field and
// potentially many Field objects pointing to it.)
private Field root;
+ // This is set by the vm at Field creation
+ private byte[] typeAnnotations;
// Generics infrastructure
@@ -144,6 +146,8 @@
// Might as well eagerly propagate this if already present
res.fieldAccessor = fieldAccessor;
res.overrideFieldAccessor = overrideFieldAccessor;
+
+ res.typeAnnotations = typeAnnotations;
return res;
}
--- a/jdk/src/share/classes/java/lang/reflect/Member.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/lang/reflect/Member.java Fri Dec 28 18:36:41 2012 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2012, 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
@@ -87,6 +87,7 @@
*
* @return true if and only if this member was introduced by
* the compiler.
+ * @jls 13.1 The Form of a Binary
* @since 1.5
*/
public boolean isSynthetic();
--- a/jdk/src/share/classes/java/lang/reflect/Method.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/lang/reflect/Method.java Fri Dec 28 18:36:41 2012 -0800
@@ -79,7 +79,8 @@
// currently only two levels deep (i.e., one root Method and
// potentially many Method objects pointing to it.)
private Method root;
-
+ // This is set by the vm at Method creation
+ private byte[] typeAnnotations;
// Generics infrastructure
private String getGenericSignature() {return signature;}
@@ -150,6 +151,8 @@
res.root = this;
// Might as well eagerly propagate this if already present
res.methodAccessor = methodAccessor;
+
+ res.typeAnnotations = typeAnnotations;
return res;
}
@@ -497,6 +500,7 @@
/**
* {@inheritDoc}
+ * @jls 13.1 The Form of a Binary
* @since 1.5
*/
@Override
@@ -504,6 +508,22 @@
return super.isSynthetic();
}
+ /**
+ * Returns {@code true} if this method is a default
+ * method; returns {@code false} otherwise.
+ *
+ * A default method is a non-abstract method, that is, a method
+ * with a body, declared in an interface type.
+ *
+ * @return true if and only if this method is a default
+ * method as defined by the Java Language Specification.
+ * @since 1.8
+ */
+ public boolean isDefault() {
+ return (getModifiers() & Modifier.ABSTRACT) == 0 &&
+ getDeclaringClass().isInterface();
+ }
+
// NOTE that there is no synchronization used here. It is correct
// (though not efficient) to generate more than one MethodAccessor
// for a given Method. However, avoiding synchronization will
--- a/jdk/src/share/classes/java/net/URLConnection.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/net/URLConnection.java Fri Dec 28 18:36:41 2012 -0800
@@ -129,15 +129,6 @@
* <a href="http://www.ietf.org/rfc/rfc2616.txt">http://www.ietf.org/rfc/rfc2616.txt</a>
* </pre></blockquote>
*
- * Note about <code>fileNameMap</code>: In versions prior to JDK 1.1.6,
- * field <code>fileNameMap</code> of <code>URLConnection</code> was public.
- * In JDK 1.1.6 and later, <code>fileNameMap</code> is private; accessor
- * and mutator methods {@link #getFileNameMap() getFileNameMap} and
- * {@link #setFileNameMap(java.net.FileNameMap) setFileNameMap} are added
- * to access it. This change is also described on the <a href=
- * "http://java.sun.com/products/jdk/1.2/compatibility.html">
- * Compatibility</a> page.
- *
* Invoking the <tt>close()</tt> methods on the <tt>InputStream</tt> or <tt>OutputStream</tt> of an
* <tt>URLConnection</tt> after a request may free network resources associated with this
* instance, unless particular protocol specifications specify different behaviours
@@ -305,8 +296,7 @@
* Loads filename map (a mimetable) from a data file. It will
* first try to load the user-specific table, defined
* by "content.types.user.table" property. If that fails,
- * it tries to load the default built-in table at
- * lib/content-types.properties under java home.
+ * it tries to load the default built-in table.
*
* @return the FileNameMap
* @since 1.2
--- a/jdk/src/share/classes/java/util/Arrays.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/util/Arrays.java Fri Dec 28 18:36:41 2012 -0800
@@ -26,6 +26,7 @@
package java.util;
import java.lang.reflect.*;
+import static java.util.ArraysParallelSortHelpers.*;
/**
* This class contains various methods for manipulating arrays (such as
@@ -54,6 +55,13 @@
*/
public class Arrays {
+ /**
+ * The minimum array length below which the sorting algorithm will not
+ * further partition the sorting task.
+ */
+ // reasonable default so that we don't overcreate tasks
+ private static final int MIN_ARRAY_SORT_GRAN = 256;
+
// Suppresses default constructor, ensuring non-instantiability.
private Arrays() {}
@@ -787,6 +795,613 @@
}
}
+ /*
+ * Parallel sorting of primitive type arrays.
+ */
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(byte[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(byte[] a) {
+ parallelSort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(byte[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(byte[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ int nelements = toIndex - fromIndex;
+ int gran = getSplitThreshold(nelements);
+ FJByte.Sorter task = new FJByte.Sorter(a, new byte[a.length], fromIndex,
+ nelements, gran);
+ task.invoke();
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(char[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(char[] a) {
+ parallelSort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(char[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(char[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ int nelements = toIndex - fromIndex;
+ int gran = getSplitThreshold(nelements);
+ FJChar.Sorter task = new FJChar.Sorter(a, new char[a.length], fromIndex,
+ nelements, gran);
+ task.invoke();
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(short[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(short[] a) {
+ parallelSort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(short[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(short[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ int nelements = toIndex - fromIndex;
+ int gran = getSplitThreshold(nelements);
+ FJShort.Sorter task = new FJShort.Sorter(a, new short[a.length], fromIndex,
+ nelements, gran);
+ task.invoke();
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(int[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(int[] a) {
+ parallelSort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(int[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(int[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ int nelements = toIndex - fromIndex;
+ int gran = getSplitThreshold(nelements);
+ FJInt.Sorter task = new FJInt.Sorter(a, new int[a.length], fromIndex,
+ nelements, gran);
+ task.invoke();
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(long[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(long[] a) {
+ parallelSort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(long[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(long[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ int nelements = toIndex - fromIndex;
+ int gran = getSplitThreshold(nelements);
+ FJLong.Sorter task = new FJLong.Sorter(a, new long[a.length], fromIndex,
+ nelements, gran);
+ task.invoke();
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * <p>The {@code <} relation does not provide a total order on all float
+ * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
+ * {@code 0.0f} and {@code Float.NaN} is considered greater than any
+ * other value and all {@code Float.NaN} values are considered equal.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(float[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(float[] a) {
+ parallelSort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * <p>The {@code <} relation does not provide a total order on all float
+ * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
+ * {@code 0.0f} and {@code Float.NaN} is considered greater than any
+ * other value and all {@code Float.NaN} values are considered equal.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(float[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(float[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ int nelements = toIndex - fromIndex;
+ int gran = getSplitThreshold(nelements);
+ FJFloat.Sorter task = new FJFloat.Sorter(a, new float[a.length], fromIndex,
+ nelements, gran);
+ task.invoke();
+ }
+
+ /**
+ * Sorts the specified array into ascending numerical order.
+ *
+ * <p>The {@code <} relation does not provide a total order on all double
+ * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
+ * {@code 0.0d} and {@code Double.NaN} is considered greater than any
+ * other value and all {@code Double.NaN} values are considered equal.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(double[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(double[] a) {
+ parallelSort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the array into ascending order. The range
+ * to be sorted extends from the index {@code fromIndex}, inclusive, to
+ * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
+ * the range to be sorted is empty.
+ *
+ * <p>The {@code <} relation does not provide a total order on all double
+ * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
+ * value compares neither less than, greater than, nor equal to any value,
+ * even itself. This method uses the total order imposed by the method
+ * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
+ * {@code 0.0d} and {@code Double.NaN} is considered greater than any
+ * other value and all {@code Double.NaN} values are considered equal.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(double[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element, inclusive, to be sorted
+ * @param toIndex the index of the last element, exclusive, to be sorted
+ *
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex}
+ * @throws ArrayIndexOutOfBoundsException
+ * if {@code fromIndex < 0} or {@code toIndex > a.length}
+ *
+ * @since 1.8
+ */
+ public static void parallelSort(double[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ int nelements = toIndex - fromIndex;
+ int gran = getSplitThreshold(nelements);
+ FJDouble.Sorter task = new FJDouble.Sorter(a, new double[a.length],
+ fromIndex, nelements, gran);
+ task.invoke();
+ }
+
+ /*
+ * Parallel sorting of complex type arrays.
+ */
+
+ /**
+ * Sorts the specified array of objects into ascending order, according
+ * to the {@linkplain Comparable natural ordering} of its elements.
+ * All elements in the array must implement the {@link Comparable}
+ * interface. Furthermore, all elements in the array must be
+ * <i>mutually comparable</i> (that is, {@code e1.compareTo(e2)} must
+ * not throw a {@code ClassCastException} for any elements {@code e1}
+ * and {@code e2} in the array).
+ *
+ * <p>This sort is not guaranteed to be <i>stable</i>: equal elements
+ * may be reordered as a result of the sort.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(Object[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ *
+ * @throws ClassCastException if the array contains elements that are not
+ * <i>mutually comparable</i> (for example, strings and integers)
+ * @throws IllegalArgumentException (optional) if the natural
+ * ordering of the array elements is found to violate the
+ * {@link Comparable} contract
+ *
+ * @since 1.8
+ */
+ public static <T extends Comparable<? super T>> void parallelSort(T[] a) {
+ parallelSort(a, 0, a.length);
+ }
+
+ /**
+ * Sorts the specified range of the specified array of objects into
+ * ascending order, according to the
+ * {@linkplain Comparable natural ordering} of its
+ * elements. The range to be sorted extends from index
+ * {@code fromIndex}, inclusive, to index {@code toIndex}, exclusive.
+ * (If {@code fromIndex==toIndex}, the range to be sorted is empty.) All
+ * elements in this range must implement the {@link Comparable}
+ * interface. Furthermore, all elements in this range must be <i>mutually
+ * comparable</i> (that is, {@code e1.compareTo(e2)} must not throw a
+ * {@code ClassCastException} for any elements {@code e1} and
+ * {@code e2} in the array).
+ *
+ * <p>This sort is not guaranteed to be <i>stable</i>: equal elements
+ * may be reordered as a result of the sort.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(Object[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element (inclusive) to be
+ * sorted
+ * @param toIndex the index of the last element (exclusive) to be sorted
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex} or
+ * (optional) if the natural ordering of the array elements is
+ * found to violate the {@link Comparable} contract
+ * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
+ * {@code toIndex > a.length}
+ * @throws ClassCastException if the array contains elements that are
+ * not <i>mutually comparable</i> (for example, strings and
+ * integers).
+ *
+ * @since 1.8
+ */
+ public static <T extends Comparable<? super T>>
+ void parallelSort(T[] a, int fromIndex, int toIndex) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ int nelements = toIndex - fromIndex;
+ Class<?> tc = a.getClass().getComponentType();
+ @SuppressWarnings("unchecked")
+ T[] workspace = (T[])Array.newInstance(tc, a.length);
+ int gran = getSplitThreshold(nelements);
+ FJComparable.Sorter<T> task = new FJComparable.Sorter<>(a, workspace,
+ fromIndex,
+ nelements, gran);
+ task.invoke();
+ }
+
+ /**
+ * Sorts the specified array of objects according to the order induced by
+ * the specified comparator. All elements in the array must be
+ * <i>mutually comparable</i> by the specified comparator (that is,
+ * {@code c.compare(e1, e2)} must not throw a {@code ClassCastException}
+ * for any elements {@code e1} and {@code e2} in the array).
+ *
+ * <p>This sort is not guaranteed to be <i>stable</i>: equal elements
+ * may be reordered as a result of the sort.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(Object[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ * @param c the comparator to determine the order of the array. A
+ * {@code null} value indicates that the elements'
+ * {@linkplain Comparable natural ordering} should be used.
+ * @throws ClassCastException if the array contains elements that are
+ * not <i>mutually comparable</i> using the specified comparator
+ * @throws IllegalArgumentException (optional) if the comparator is
+ * found to violate the {@link java.util.Comparator} contract
+ *
+ * @since 1.8
+ */
+ public static <T> void parallelSort(T[] a, Comparator<? super T> c) {
+ parallelSort(a, 0, a.length, c);
+ }
+
+ /**
+ * Sorts the specified range of the specified array of objects according
+ * to the order induced by the specified comparator. The range to be
+ * sorted extends from index {@code fromIndex}, inclusive, to index
+ * {@code toIndex}, exclusive. (If {@code fromIndex==toIndex}, the
+ * range to be sorted is empty.) All elements in the range must be
+ * <i>mutually comparable</i> by the specified comparator (that is,
+ * {@code c.compare(e1, e2)} must not throw a {@code ClassCastException}
+ * for any elements {@code e1} and {@code e2} in the range).
+ *
+ * <p>This sort is not guaranteed to be <i>stable</i>: equal elements
+ * may be reordered as a result of the sort.
+ *
+ * <p>Implementation note: The sorting algorithm is a parallel sort-merge
+ * that breaks the array into sub-arrays that are themselves sorted and then
+ * merged. When the sub-array length reaches a minimum granularity, the
+ * sub-array is sorted using the appropriate {@link Arrays#sort(Object[])
+ * Arrays.sort} method. The algorithm requires a working space equal to the
+ * size of the original array. The {@link
+ * java.util.concurrent.ForkJoinPool#commonPool() ForkJoin common pool} is
+ * used to execute any parallel tasks.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element (inclusive) to be
+ * sorted
+ * @param toIndex the index of the last element (exclusive) to be sorted
+ * @param c the comparator to determine the order of the array. A
+ * {@code null} value indicates that the elements'
+ * {@linkplain Comparable natural ordering} should be used.
+ * @throws IllegalArgumentException if {@code fromIndex > toIndex} or
+ * (optional) if the natural ordering of the array elements is
+ * found to violate the {@link Comparable} contract
+ * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
+ * {@code toIndex > a.length}
+ * @throws ClassCastException if the array contains elements that are
+ * not <i>mutually comparable</i> (for example, strings and
+ * integers).
+ *
+ * @since 1.8
+ */
+ public static <T> void parallelSort(T[] a, int fromIndex, int toIndex,
+ Comparator<? super T> c) {
+ rangeCheck(a.length, fromIndex, toIndex);
+ int nelements = toIndex - fromIndex;
+ Class<?> tc = a.getClass().getComponentType();
+ @SuppressWarnings("unchecked")
+ T[] workspace = (T[])Array.newInstance(tc, a.length);
+ int gran = getSplitThreshold(nelements);
+ FJComparator.Sorter<T> task = new FJComparator.Sorter<>(a, workspace,
+ fromIndex,
+ nelements, gran, c);
+ task.invoke();
+ }
+
+ /**
+ * Returns the size threshold for splitting into subtasks.
+ * By default, uses about 8 times as many tasks as threads
+ *
+ * @param n number of elements in the array to be processed
+ */
+ private static int getSplitThreshold(int n) {
+ int p = java.util.concurrent.ForkJoinPool.getCommonPoolParallelism();
+ int t = (p > 1) ? (1 + n / (p << 3)) : n;
+ return t < MIN_ARRAY_SORT_GRAN ? MIN_ARRAY_SORT_GRAN : t;
+ }
+
/**
* Checks that {@code fromIndex} and {@code toIndex} are in
* the range and throws an appropriate exception, if they aren't.
@@ -1480,9 +2095,9 @@
while (low <= high) {
int mid = (low + high) >>> 1;
@SuppressWarnings("rawtypes")
- Comparable midVal = (Comparable)a[mid];
+ Comparable midVal = (Comparable)a[mid];
@SuppressWarnings("unchecked")
- int cmp = midVal.compareTo(key);
+ int cmp = midVal.compareTo(key);
if (cmp < 0)
low = mid + 1;
@@ -2847,19 +3462,20 @@
private final E[] a;
ArrayList(E[] array) {
- if (array==null)
- throw new NullPointerException();
- a = array;
+ a = Objects.requireNonNull(array);
}
+ @Override
public int size() {
return a.length;
}
+ @Override
public Object[] toArray() {
return a.clone();
}
+ @Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
@@ -2872,16 +3488,19 @@
return a;
}
+ @Override
public E get(int index) {
return a[index];
}
+ @Override
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
+ @Override
public int indexOf(Object o) {
if (o==null) {
for (int i=0; i<a.length; i++)
@@ -2895,6 +3514,7 @@
return -1;
}
+ @Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/util/ArraysParallelSortHelpers.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,1223 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package java.util;
+
+import java.util.concurrent.RecursiveAction;
+
+/**
+ * Helper utilities for the parallel sort methods in Arrays.parallelSort.
+ *
+ * For each primitive type, plus Object, we define a static class to
+ * contain the Sorter and Merger implementations for that type:
+ *
+ * Sorter classes based mainly on CilkSort
+ * <A href="http://supertech.lcs.mit.edu/cilk/"> Cilk</A>:
+ * Basic algorithm:
+ * if array size is small, just use a sequential quicksort (via Arrays.sort)
+ * Otherwise:
+ * 1. Break array in half.
+ * 2. For each half,
+ * a. break the half in half (i.e., quarters),
+ * b. sort the quarters
+ * c. merge them together
+ * 3. merge together the two halves.
+ *
+ * One reason for splitting in quarters is that this guarantees
+ * that the final sort is in the main array, not the workspace
+ * array. (workspace and main swap roles on each subsort step.)
+ * Leaf-level sorts use a Sequential quicksort, that in turn uses
+ * insertion sort if under threshold. Otherwise it uses median of
+ * three to pick pivot, and loops rather than recurses along left
+ * path.
+ *
+ *
+ * Merger classes perform merging for Sorter. If big enough, splits Left
+ * partition in half; finds the greatest point in Right partition
+ * less than the beginning of the second half of Left via binary
+ * search; and then, in parallel, merges left half of Left with
+ * elements of Right up to split point, and merges right half of
+ * Left with elements of R past split point. At leaf, it just
+ * sequentially merges. This is all messy to code; sadly we need
+ * distinct versions for each type.
+ *
+ */
+/*package*/ class ArraysParallelSortHelpers {
+
+ // RFE: we should only need a working array as large as the subarray
+ // to be sorted, but the logic assumes that indices in the two
+ // arrays always line-up
+
+ /** byte support class */
+ static final class FJByte {
+ static final class Sorter extends RecursiveAction {
+ static final long serialVersionUID = 749471161188027634L;
+ final byte[] a; // array to be sorted.
+ final byte[] w; // workspace for merge
+ final int origin; // origin of the part of array we deal with
+ final int n; // Number of elements in (sub)arrays.
+ final int gran; // split control
+
+ Sorter(byte[] a, byte[] w, int origin, int n, int gran) {
+ this.a = a;
+ this.w = w;
+ this.origin = origin;
+ this.n = n;
+ this.gran = gran;
+ }
+
+ public void compute() {
+ final int l = origin;
+ final int g = gran;
+ final int n = this.n;
+ final byte[] a = this.a;
+ final byte[] w = this.w;
+ if (n > g) {
+ int h = n >>> 1; // half
+ int q = n >>> 2; // lower quarter index
+ int u = h + q; // upper quarter
+ FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g),
+ new Sorter(a, w, l+q, h-q, g),
+ new Merger(a, w, l, q,
+ l+q, h-q, l, g, null));
+ FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l+h, q, g),
+ new Sorter(a, w, l+u, n-u, g),
+ new Merger(a, w, l+h, q,
+ l+u, n-u, l+h, g, null));
+ rs.fork();
+ ls.compute();
+ if (rs.tryUnfork()) rs.compute(); else rs.join();
+ new Merger(w, a, l, h,
+ l+h, n-h, l, g, null).compute();
+ } else {
+ DualPivotQuicksort.sort(a, l, l+n-1); //skip rangeCheck
+ }
+ }
+ }
+
+ static final class Merger extends RecursiveAction {
+ static final long serialVersionUID = -9090258248781844470L;
+ final byte[] a;
+ final byte[] w;
+ final int lo;
+ final int ln;
+ final int ro;
+ final int rn;
+ final int wo;
+ final int gran;
+ final Merger next;
+
+ Merger(byte[] a, byte[] w, int lo, int ln, int ro, int rn, int wo,
+ int gran, Merger next) {
+ this.a = a;
+ this.w = w;
+ this.lo = lo;
+ this.ln = ln;
+ this.ro = ro;
+ this.rn = rn;
+ this.wo = wo;
+ this.gran = gran;
+ this.next = next;
+ }
+
+ public void compute() {
+ final byte[] a = this.a;
+ final byte[] w = this.w;
+ Merger rights = null;
+ int nleft = ln;
+ int nright = rn;
+ while (nleft > gran) {
+ int lh = nleft >>> 1;
+ int splitIndex = lo + lh;
+ byte split = a[splitIndex];
+ int rl = 0;
+ int rh = nright;
+ while (rl < rh) {
+ int mid = (rl + rh) >>> 1;
+ if (split <= a[ro + mid])
+ rh = mid;
+ else
+ rl = mid + 1;
+ }
+ (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh,
+ nright-rh, wo+lh+rh, gran, rights)).fork();
+ nleft = lh;
+ nright = rh;
+ }
+
+ int l = lo;
+ int lFence = l + nleft;
+ int r = ro;
+ int rFence = r + nright;
+ int k = wo;
+ while (l < lFence && r < rFence) {
+ byte al = a[l];
+ byte ar = a[r];
+ byte t;
+ if (al <= ar) {++l; t=al;} else {++r; t = ar;}
+ w[k++] = t;
+ }
+ while (l < lFence)
+ w[k++] = a[l++];
+ while (r < rFence)
+ w[k++] = a[r++];
+ while (rights != null) {
+ if (rights.tryUnfork())
+ rights.compute();
+ else
+ rights.join();
+ rights = rights.next;
+ }
+ }
+ }
+ } // FJByte
+
+ /** char support class */
+ static final class FJChar {
+ static final class Sorter extends RecursiveAction {
+ static final long serialVersionUID = 8723376019074596641L;
+ final char[] a; // array to be sorted.
+ final char[] w; // workspace for merge
+ final int origin; // origin of the part of array we deal with
+ final int n; // Number of elements in (sub)arrays.
+ final int gran; // split control
+
+ Sorter(char[] a, char[] w, int origin, int n, int gran) {
+ this.a = a;
+ this.w = w;
+ this.origin = origin;
+ this.n = n;
+ this.gran = gran;
+ }
+
+ public void compute() {
+ final int l = origin;
+ final int g = gran;
+ final int n = this.n;
+ final char[] a = this.a;
+ final char[] w = this.w;
+ if (n > g) {
+ int h = n >>> 1; // half
+ int q = n >>> 2; // lower quarter index
+ int u = h + q; // upper quarter
+ FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g),
+ new Sorter(a, w, l+q, h-q, g),
+ new Merger(a, w, l, q,
+ l+q, h-q, l, g, null));
+ FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g),
+ new Sorter(a, w, l+u, n-u, g),
+ new Merger(a, w, l+h, q,
+ l+u, n-u, l+h, g, null));
+ rs.fork();
+ ls.compute();
+ if (rs.tryUnfork()) rs.compute(); else rs.join();
+ new Merger(w, a, l, h, l + h, n - h, l, g, null).compute();
+ } else {
+ DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck
+ }
+ }
+ }
+
+ static final class Merger extends RecursiveAction {
+ static final long serialVersionUID = -1383975444621698926L;
+ final char[] a;
+ final char[] w;
+ final int lo;
+ final int ln;
+ final int ro;
+ final int rn;
+ final int wo;
+ final int gran;
+ final Merger next;
+
+ Merger(char[] a, char[] w, int lo, int ln, int ro, int rn, int wo,
+ int gran, Merger next) {
+ this.a = a;
+ this.w = w;
+ this.lo = lo;
+ this.ln = ln;
+ this.ro = ro;
+ this.rn = rn;
+ this.wo = wo;
+ this.gran = gran;
+ this.next = next;
+ }
+
+ public void compute() {
+ final char[] a = this.a;
+ final char[] w = this.w;
+ Merger rights = null;
+ int nleft = ln;
+ int nright = rn;
+ while (nleft > gran) {
+ int lh = nleft >>> 1;
+ int splitIndex = lo + lh;
+ char split = a[splitIndex];
+ int rl = 0;
+ int rh = nright;
+ while (rl < rh) {
+ int mid = (rl + rh) >>> 1;
+ if (split <= a[ro + mid])
+ rh = mid;
+ else
+ rl = mid + 1;
+ }
+ (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh,
+ nright-rh, wo+lh+rh, gran, rights)).fork();
+ nleft = lh;
+ nright = rh;
+ }
+
+ int l = lo;
+ int lFence = l + nleft;
+ int r = ro;
+ int rFence = r + nright;
+ int k = wo;
+ while (l < lFence && r < rFence) {
+ char al = a[l];
+ char ar = a[r];
+ char t;
+ if (al <= ar) {++l; t=al;} else {++r; t = ar;}
+ w[k++] = t;
+ }
+ while (l < lFence)
+ w[k++] = a[l++];
+ while (r < rFence)
+ w[k++] = a[r++];
+ while (rights != null) {
+ if (rights.tryUnfork())
+ rights.compute();
+ else
+ rights.join();
+ rights = rights.next;
+ }
+ }
+ }
+ } // FJChar
+
+ /** short support class */
+ static final class FJShort {
+ static final class Sorter extends RecursiveAction {
+ static final long serialVersionUID = -7886754793730583084L;
+ final short[] a; // array to be sorted.
+ final short[] w; // workspace for merge
+ final int origin; // origin of the part of array we deal with
+ final int n; // Number of elements in (sub)arrays.
+ final int gran; // split control
+
+ Sorter(short[] a, short[] w, int origin, int n, int gran) {
+ this.a = a;
+ this.w = w;
+ this.origin = origin;
+ this.n = n;
+ this.gran = gran;
+ }
+
+ public void compute() {
+ final int l = origin;
+ final int g = gran;
+ final int n = this.n;
+ final short[] a = this.a;
+ final short[] w = this.w;
+ if (n > g) {
+ int h = n >>> 1; // half
+ int q = n >>> 2; // lower quarter index
+ int u = h + q; // upper quarter
+ FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g),
+ new Sorter(a, w, l+q, h-q, g),
+ new Merger(a, w, l, q,
+ l+q, h-q, l, g, null));
+ FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g),
+ new Sorter(a, w, l+u, n-u, g),
+ new Merger(a, w, l+h, q,
+ l+u, n-u, l+h, g, null));
+ rs.fork();
+ ls.compute();
+ if (rs.tryUnfork()) rs.compute(); else rs.join();
+ new Merger(w, a, l, h, l + h, n - h, l, g, null).compute();
+ } else {
+ DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck
+ }
+ }
+ }
+
+ static final class Merger extends RecursiveAction {
+ static final long serialVersionUID = 3895749408536700048L;
+ final short[] a;
+ final short[] w;
+ final int lo;
+ final int ln;
+ final int ro;
+ final int rn;
+ final int wo;
+ final int gran;
+ final Merger next;
+
+ Merger(short[] a, short[] w, int lo, int ln, int ro, int rn, int wo,
+ int gran, Merger next) {
+ this.a = a;
+ this.w = w;
+ this.lo = lo;
+ this.ln = ln;
+ this.ro = ro;
+ this.rn = rn;
+ this.wo = wo;
+ this.gran = gran;
+ this.next = next;
+ }
+
+ public void compute() {
+ final short[] a = this.a;
+ final short[] w = this.w;
+ Merger rights = null;
+ int nleft = ln;
+ int nright = rn;
+ while (nleft > gran) {
+ int lh = nleft >>> 1;
+ int splitIndex = lo + lh;
+ short split = a[splitIndex];
+ int rl = 0;
+ int rh = nright;
+ while (rl < rh) {
+ int mid = (rl + rh) >>> 1;
+ if (split <= a[ro + mid])
+ rh = mid;
+ else
+ rl = mid + 1;
+ }
+ (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh,
+ nright-rh, wo+lh+rh, gran, rights)).fork();
+ nleft = lh;
+ nright = rh;
+ }
+
+ int l = lo;
+ int lFence = l + nleft;
+ int r = ro;
+ int rFence = r + nright;
+ int k = wo;
+ while (l < lFence && r < rFence) {
+ short al = a[l];
+ short ar = a[r];
+ short t;
+ if (al <= ar) {++l; t=al;} else {++r; t = ar;}
+ w[k++] = t;
+ }
+ while (l < lFence)
+ w[k++] = a[l++];
+ while (r < rFence)
+ w[k++] = a[r++];
+ while (rights != null) {
+ if (rights.tryUnfork())
+ rights.compute();
+ else
+ rights.join();
+ rights = rights.next;
+ }
+ }
+ }
+ } // FJShort
+
+ /** int support class */
+ static final class FJInt {
+ static final class Sorter extends RecursiveAction {
+ static final long serialVersionUID = 4263311808957292729L;
+ final int[] a; // array to be sorted.
+ final int[] w; // workspace for merge
+ final int origin; // origin of the part of array we deal with
+ final int n; // Number of elements in (sub)arrays.
+ final int gran; // split control
+
+ Sorter(int[] a, int[] w, int origin, int n, int gran) {
+ this.a = a;
+ this.w = w;
+ this.origin = origin;
+ this.n = n;
+ this.gran = gran;
+ }
+
+ public void compute() {
+ final int l = origin;
+ final int g = gran;
+ final int n = this.n;
+ final int[] a = this.a;
+ final int[] w = this.w;
+ if (n > g) {
+ int h = n >>> 1; // half
+ int q = n >>> 2; // lower quarter index
+ int u = h + q; // upper quarter
+ FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g),
+ new Sorter(a, w, l+q, h-q, g),
+ new Merger(a, w, l, q,
+ l+q, h-q, l, g, null));
+ FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g),
+ new Sorter(a, w, l+u, n-u, g),
+ new Merger(a, w, l+h, q,
+ l+u, n-u, l+h, g, null));
+ rs.fork();
+ ls.compute();
+ if (rs.tryUnfork()) rs.compute(); else rs.join();
+ new Merger(w, a, l, h, l + h, n - h, l, g, null).compute();
+ } else {
+ DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck
+ }
+ }
+ }
+
+ static final class Merger extends RecursiveAction {
+ static final long serialVersionUID = -8727507284219982792L;
+ final int[] a;
+ final int[] w;
+ final int lo;
+ final int ln;
+ final int ro;
+ final int rn;
+ final int wo;
+ final int gran;
+ final Merger next;
+
+ Merger(int[] a, int[] w, int lo, int ln, int ro, int rn, int wo,
+ int gran, Merger next) {
+ this.a = a;
+ this.w = w;
+ this.lo = lo;
+ this.ln = ln;
+ this.ro = ro;
+ this.rn = rn;
+ this.wo = wo;
+ this.gran = gran;
+ this.next = next;
+ }
+
+ public void compute() {
+ final int[] a = this.a;
+ final int[] w = this.w;
+ Merger rights = null;
+ int nleft = ln;
+ int nright = rn;
+ while (nleft > gran) {
+ int lh = nleft >>> 1;
+ int splitIndex = lo + lh;
+ int split = a[splitIndex];
+ int rl = 0;
+ int rh = nright;
+ while (rl < rh) {
+ int mid = (rl + rh) >>> 1;
+ if (split <= a[ro + mid])
+ rh = mid;
+ else
+ rl = mid + 1;
+ }
+ (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh,
+ nright-rh, wo+lh+rh, gran, rights)).fork();
+ nleft = lh;
+ nright = rh;
+ }
+
+ int l = lo;
+ int lFence = l + nleft;
+ int r = ro;
+ int rFence = r + nright;
+ int k = wo;
+ while (l < lFence && r < rFence) {
+ int al = a[l];
+ int ar = a[r];
+ int t;
+ if (al <= ar) {++l; t=al;} else {++r; t = ar;}
+ w[k++] = t;
+ }
+ while (l < lFence)
+ w[k++] = a[l++];
+ while (r < rFence)
+ w[k++] = a[r++];
+ while (rights != null) {
+ if (rights.tryUnfork())
+ rights.compute();
+ else
+ rights.join();
+ rights = rights.next;
+ }
+ }
+ }
+ } // FJInt
+
+ /** long support class */
+ static final class FJLong {
+ static final class Sorter extends RecursiveAction {
+ static final long serialVersionUID = 6553695007444392455L;
+ final long[] a; // array to be sorted.
+ final long[] w; // workspace for merge
+ final int origin; // origin of the part of array we deal with
+ final int n; // Number of elements in (sub)arrays.
+ final int gran; // split control
+
+ Sorter(long[] a, long[] w, int origin, int n, int gran) {
+ this.a = a;
+ this.w = w;
+ this.origin = origin;
+ this.n = n;
+ this.gran = gran;
+ }
+
+ public void compute() {
+ final int l = origin;
+ final int g = gran;
+ final int n = this.n;
+ final long[] a = this.a;
+ final long[] w = this.w;
+ if (n > g) {
+ int h = n >>> 1; // half
+ int q = n >>> 2; // lower quarter index
+ int u = h + q; // upper quarter
+ FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g),
+ new Sorter(a, w, l+q, h-q, g),
+ new Merger(a, w, l, q,
+ l+q, h-q, l, g, null));
+ FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g),
+ new Sorter(a, w, l+u, n-u, g),
+ new Merger(a, w, l+h, q,
+ l+u, n-u, l+h, g, null));
+ rs.fork();
+ ls.compute();
+ if (rs.tryUnfork()) rs.compute(); else rs.join();
+ new Merger(w, a, l, h, l + h, n - h, l, g, null).compute();
+ } else {
+ DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck
+ }
+ }
+ }
+
+ static final class Merger extends RecursiveAction {
+ static final long serialVersionUID = 8843567516333283861L;
+ final long[] a;
+ final long[] w;
+ final int lo;
+ final int ln;
+ final int ro;
+ final int rn;
+ final int wo;
+ final int gran;
+ final Merger next;
+
+ Merger(long[] a, long[] w, int lo, int ln, int ro, int rn, int wo,
+ int gran, Merger next) {
+ this.a = a;
+ this.w = w;
+ this.lo = lo;
+ this.ln = ln;
+ this.ro = ro;
+ this.rn = rn;
+ this.wo = wo;
+ this.gran = gran;
+ this.next = next;
+ }
+
+ public void compute() {
+ final long[] a = this.a;
+ final long[] w = this.w;
+ Merger rights = null;
+ int nleft = ln;
+ int nright = rn;
+ while (nleft > gran) {
+ int lh = nleft >>> 1;
+ int splitIndex = lo + lh;
+ long split = a[splitIndex];
+ int rl = 0;
+ int rh = nright;
+ while (rl < rh) {
+ int mid = (rl + rh) >>> 1;
+ if (split <= a[ro + mid])
+ rh = mid;
+ else
+ rl = mid + 1;
+ }
+ (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh,
+ nright-rh, wo+lh+rh, gran, rights)).fork();
+ nleft = lh;
+ nright = rh;
+ }
+
+ int l = lo;
+ int lFence = l + nleft;
+ int r = ro;
+ int rFence = r + nright;
+ int k = wo;
+ while (l < lFence && r < rFence) {
+ long al = a[l];
+ long ar = a[r];
+ long t;
+ if (al <= ar) {++l; t=al;} else {++r; t = ar;}
+ w[k++] = t;
+ }
+ while (l < lFence)
+ w[k++] = a[l++];
+ while (r < rFence)
+ w[k++] = a[r++];
+ while (rights != null) {
+ if (rights.tryUnfork())
+ rights.compute();
+ else
+ rights.join();
+ rights = rights.next;
+ }
+ }
+ }
+ } // FJLong
+
+ /** float support class */
+ static final class FJFloat {
+ static final class Sorter extends RecursiveAction {
+ static final long serialVersionUID = 1602600178202763377L;
+ final float[] a; // array to be sorted.
+ final float[] w; // workspace for merge
+ final int origin; // origin of the part of array we deal with
+ final int n; // Number of elements in (sub)arrays.
+ final int gran; // split control
+
+ Sorter(float[] a, float[] w, int origin, int n, int gran) {
+ this.a = a;
+ this.w = w;
+ this.origin = origin;
+ this.n = n;
+ this.gran = gran;
+ }
+
+ public void compute() {
+ final int l = origin;
+ final int g = gran;
+ final int n = this.n;
+ final float[] a = this.a;
+ final float[] w = this.w;
+ if (n > g) {
+ int h = n >>> 1; // half
+ int q = n >>> 2; // lower quarter index
+ int u = h + q; // upper quarter
+ FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g),
+ new Sorter(a, w, l+q, h-q, g),
+ new Merger(a, w, l, q,
+ l+q, h-q, l, g, null));
+ FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g),
+ new Sorter(a, w, l+u, n-u, g),
+ new Merger(a, w, l+h, q,
+ l+u, n-u, l+h, g, null));
+ rs.fork();
+ ls.compute();
+ if (rs.tryUnfork()) rs.compute(); else rs.join();
+ new Merger(w, a, l, h, l + h, n - h, l, g, null).compute();
+ } else {
+ DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck
+ }
+ }
+ }
+
+ static final class Merger extends RecursiveAction {
+ static final long serialVersionUID = 1518176433845397426L;
+ final float[] a;
+ final float[] w;
+ final int lo;
+ final int ln;
+ final int ro;
+ final int rn;
+ final int wo;
+ final int gran;
+ final Merger next;
+
+ Merger(float[] a, float[] w, int lo, int ln, int ro, int rn, int wo,
+ int gran, Merger next) {
+ this.a = a;
+ this.w = w;
+ this.lo = lo;
+ this.ln = ln;
+ this.ro = ro;
+ this.rn = rn;
+ this.wo = wo;
+ this.gran = gran;
+ this.next = next;
+ }
+
+ public void compute() {
+ final float[] a = this.a;
+ final float[] w = this.w;
+ Merger rights = null;
+ int nleft = ln;
+ int nright = rn;
+ while (nleft > gran) {
+ int lh = nleft >>> 1;
+ int splitIndex = lo + lh;
+ float split = a[splitIndex];
+ int rl = 0;
+ int rh = nright;
+ while (rl < rh) {
+ int mid = (rl + rh) >>> 1;
+ if (Float.compare(split, a[ro+mid]) <= 0)
+ rh = mid;
+ else
+ rl = mid + 1;
+ }
+ (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh,
+ nright-rh, wo+lh+rh, gran, rights)).fork();
+ nleft = lh;
+ nright = rh;
+ }
+
+ int l = lo;
+ int lFence = l + nleft;
+ int r = ro;
+ int rFence = r + nright;
+ int k = wo;
+ while (l < lFence && r < rFence) {
+ float al = a[l];
+ float ar = a[r];
+ float t;
+ if (Float.compare(al, ar) <= 0) {
+ ++l;
+ t = al;
+ } else {
+ ++r;
+ t = ar;
+ }
+ w[k++] = t;
+ }
+ while (l < lFence)
+ w[k++] = a[l++];
+ while (r < rFence)
+ w[k++] = a[r++];
+ while (rights != null) {
+ if (rights.tryUnfork())
+ rights.compute();
+ else
+ rights.join();
+ rights = rights.next;
+ }
+ }
+ }
+ } // FJFloat
+
+ /** double support class */
+ static final class FJDouble {
+ static final class Sorter extends RecursiveAction {
+ static final long serialVersionUID = 2446542900576103244L;
+ final double[] a; // array to be sorted.
+ final double[] w; // workspace for merge
+ final int origin; // origin of the part of array we deal with
+ final int n; // Number of elements in (sub)arrays.
+ final int gran; // split control
+
+ Sorter(double[] a, double[] w, int origin, int n, int gran) {
+ this.a = a;
+ this.w = w;
+ this.origin = origin;
+ this.n = n;
+ this.gran = gran;
+ }
+
+ public void compute() {
+ final int l = origin;
+ final int g = gran;
+ final int n = this.n;
+ final double[] a = this.a;
+ final double[] w = this.w;
+ if (n > g) {
+ int h = n >>> 1; // half
+ int q = n >>> 2; // lower quarter index
+ int u = h + q; // upper quarter
+ FJSubSorter ls = new FJSubSorter(new Sorter(a, w, l, q, g),
+ new Sorter(a, w, l+q, h-q, g),
+ new Merger(a, w, l, q,
+ l+q, h-q, l, g, null));
+ FJSubSorter rs = new FJSubSorter(new Sorter(a, w, l + h, q, g),
+ new Sorter(a, w, l+u, n-u, g),
+ new Merger(a, w, l+h, q,
+ l+u, n-u, l+h, g, null));
+ rs.fork();
+ ls.compute();
+ if (rs.tryUnfork()) rs.compute(); else rs.join();
+ new Merger(w, a, l, h, l + h, n - h, l, g, null).compute();
+ } else {
+ DualPivotQuicksort.sort(a, l, l+n-1); // skip rangeCheck
+ }
+ }
+ }
+
+ static final class Merger extends RecursiveAction {
+ static final long serialVersionUID = 8076242187166127592L;
+ final double[] a;
+ final double[] w;
+ final int lo;
+ final int ln;
+ final int ro;
+ final int rn;
+ final int wo;
+ final int gran;
+ final Merger next;
+
+ Merger(double[] a, double[] w, int lo, int ln, int ro, int rn, int wo,
+ int gran, Merger next) {
+ this.a = a;
+ this.w = w;
+ this.lo = lo;
+ this.ln = ln;
+ this.ro = ro;
+ this.rn = rn;
+ this.wo = wo;
+ this.gran = gran;
+ this.next = next;
+ }
+
+ public void compute() {
+ final double[] a = this.a;
+ final double[] w = this.w;
+ Merger rights = null;
+ int nleft = ln;
+ int nright = rn;
+ while (nleft > gran) {
+ int lh = nleft >>> 1;
+ int splitIndex = lo + lh;
+ double split = a[splitIndex];
+ int rl = 0;
+ int rh = nright;
+ while (rl < rh) {
+ int mid = (rl + rh) >>> 1;
+ if (Double.compare(split, a[ro+mid]) <= 0)
+ rh = mid;
+ else
+ rl = mid + 1;
+ }
+ (rights = new Merger(a, w, splitIndex, nleft-lh, ro+rh,
+ nright-rh, wo+lh+rh, gran, rights)).fork();
+ nleft = lh;
+ nright = rh;
+ }
+
+ int l = lo;
+ int lFence = l + nleft;
+ int r = ro;
+ int rFence = r + nright;
+ int k = wo;
+ while (l < lFence && r < rFence) {
+ double al = a[l];
+ double ar = a[r];
+ double t;
+ if (Double.compare(al, ar) <= 0) {
+ ++l;
+ t = al;
+ } else {
+ ++r;
+ t = ar;
+ }
+ w[k++] = t;
+ }
+ while (l < lFence)
+ w[k++] = a[l++];
+ while (r < rFence)
+ w[k++] = a[r++];
+ while (rights != null) {
+ if (rights.tryUnfork())
+ rights.compute();
+ else
+ rights.join();
+ rights = rights.next;
+ }
+ }
+ }
+ } // FJDouble
+
+ /** Comparable support class */
+ static final class FJComparable {
+ static final class Sorter<T extends Comparable<? super T>> extends RecursiveAction {
+ static final long serialVersionUID = -1024003289463302522L;
+ final T[] a;
+ final T[] w;
+ final int origin;
+ final int n;
+ final int gran;
+
+ Sorter(T[] a, T[] w, int origin, int n, int gran) {
+ this.a = a;
+ this.w = w;
+ this.origin = origin;
+ this.n = n;
+ this.gran = gran;
+ }
+
+ public void compute() {
+ final int l = origin;
+ final int g = gran;
+ final int n = this.n;
+ final T[] a = this.a;
+ final T[] w = this.w;
+ if (n > g) {
+ int h = n >>> 1;
+ int q = n >>> 2;
+ int u = h + q;
+ FJSubSorter ls = new FJSubSorter(new Sorter<>(a, w, l, q, g),
+ new Sorter<>(a, w, l+q, h-q, g),
+ new Merger<>(a, w, l, q,
+ l+q, h-q, l, g, null));
+ FJSubSorter rs = new FJSubSorter(new Sorter<>(a, w, l+h, q, g),
+ new Sorter<>(a, w, l+u, n-u, g),
+ new Merger<>(a, w, l+h, q,
+ l+u, n-u, l+h, g, null));
+ rs.fork();
+ ls.compute();
+ if (rs.tryUnfork()) rs.compute(); else rs.join();
+ new Merger<>(w, a, l, h, l + h, n - h, l, g, null).compute();
+ } else {
+ Arrays.sort(a, l, l+n);
+ }
+ }
+ }
+
+ static final class Merger<T extends Comparable<? super T>> extends RecursiveAction {
+ static final long serialVersionUID = -3989771675258379302L;
+ final T[] a;
+ final T[] w;
+ final int lo;
+ final int ln;
+ final int ro;
+ final int rn;
+ final int wo;
+ final int gran;
+ final Merger<T> next;
+
+ Merger(T[] a, T[] w, int lo, int ln, int ro, int rn, int wo,
+ int gran, Merger<T> next) {
+ this.a = a;
+ this.w = w;
+ this.lo = lo;
+ this.ln = ln;
+ this.ro = ro;
+ this.rn = rn;
+ this.wo = wo;
+ this.gran = gran;
+ this.next = next;
+ }
+
+ public void compute() {
+ final T[] a = this.a;
+ final T[] w = this.w;
+ Merger<T> rights = null;
+ int nleft = ln;
+ int nright = rn;
+ while (nleft > gran) {
+ int lh = nleft >>> 1;
+ int splitIndex = lo + lh;
+ T split = a[splitIndex];
+ int rl = 0;
+ int rh = nright;
+ while (rl < rh) {
+ int mid = (rl + rh) >>> 1;
+ if (split.compareTo(a[ro + mid]) <= 0)
+ rh = mid;
+ else
+ rl = mid + 1;
+ }
+ (rights = new Merger<>(a, w, splitIndex, nleft-lh, ro+rh,
+ nright-rh, wo+lh+rh, gran, rights)).fork();
+ nleft = lh;
+ nright = rh;
+ }
+
+ int l = lo;
+ int lFence = l + nleft;
+ int r = ro;
+ int rFence = r + nright;
+ int k = wo;
+ while (l < lFence && r < rFence) {
+ T al = a[l];
+ T ar = a[r];
+ T t;
+ if (al.compareTo(ar) <= 0) {++l; t=al;} else {++r; t=ar; }
+ w[k++] = t;
+ }
+ while (l < lFence)
+ w[k++] = a[l++];
+ while (r < rFence)
+ w[k++] = a[r++];
+ while (rights != null) {
+ if (rights.tryUnfork())
+ rights.compute();
+ else
+ rights.join();
+ rights = rights.next;
+ }
+ }
+ }
+ } // FJComparable
+
+ /** Object + Comparator support class */
+ static final class FJComparator {
+ static final class Sorter<T> extends RecursiveAction {
+ static final long serialVersionUID = 9191600840025808581L;
+ final T[] a; // array to be sorted.
+ final T[] w; // workspace for merge
+ final int origin; // origin of the part of array we deal with
+ final int n; // Number of elements in (sub)arrays.
+ final int gran; // split control
+ final Comparator<? super T> cmp; // Comparator to use
+
+ Sorter(T[] a, T[] w, int origin, int n, int gran, Comparator<? super T> cmp) {
+ this.a = a;
+ this.w = w;
+ this.origin = origin;
+ this.n = n;
+ this.cmp = cmp;
+ this.gran = gran;
+ }
+
+ public void compute() {
+ final int l = origin;
+ final int g = gran;
+ final int n = this.n;
+ final T[] a = this.a;
+ final T[] w = this.w;
+ if (n > g) {
+ int h = n >>> 1; // half
+ int q = n >>> 2; // lower quarter index
+ int u = h + q; // upper quarter
+ FJSubSorter ls = new FJSubSorter(new Sorter<>(a, w, l, q, g, cmp),
+ new Sorter<>(a, w, l+q, h-q, g, cmp),
+ new Merger<>(a, w, l, q,
+ l+q, h-q, l, g, null, cmp));
+ FJSubSorter rs = new FJSubSorter(new Sorter<>(a, w, l + h, q, g, cmp),
+ new Sorter<>(a, w, l+u, n-u, g, cmp),
+ new Merger<>(a, w, l+h, q,
+ l+u, n-u, l+h, g, null, cmp));
+ rs.fork();
+ ls.compute();
+ if (rs.tryUnfork()) rs.compute(); else rs.join();
+ new Merger<>(w, a, l, h, l + h, n - h, l, g, null, cmp).compute();
+ } else {
+ Arrays.sort(a, l, l+n, cmp);
+ }
+ }
+ }
+
+ static final class Merger<T> extends RecursiveAction {
+ static final long serialVersionUID = -2679539040379156203L;
+ final T[] a;
+ final T[] w;
+ final int lo;
+ final int ln;
+ final int ro;
+ final int rn;
+ final int wo;
+ final int gran;
+ final Merger<T> next;
+ final Comparator<? super T> cmp;
+
+ Merger(T[] a, T[] w, int lo, int ln, int ro, int rn, int wo,
+ int gran, Merger<T> next, Comparator<? super T> cmp) {
+ this.a = a;
+ this.w = w;
+ this.lo = lo;
+ this.ln = ln;
+ this.ro = ro;
+ this.rn = rn;
+ this.wo = wo;
+ this.gran = gran;
+ this.next = next;
+ this.cmp = cmp;
+ }
+
+ public void compute() {
+ final T[] a = this.a;
+ final T[] w = this.w;
+ Merger<T> rights = null;
+ int nleft = ln;
+ int nright = rn;
+ while (nleft > gran) {
+ int lh = nleft >>> 1;
+ int splitIndex = lo + lh;
+ T split = a[splitIndex];
+ int rl = 0;
+ int rh = nright;
+ while (rl < rh) {
+ int mid = (rl + rh) >>> 1;
+ if (cmp.compare(split, a[ro+mid]) <= 0)
+ rh = mid;
+ else
+ rl = mid + 1;
+ }
+ (rights = new Merger<>(a, w, splitIndex, nleft-lh, ro+rh,
+ nright-rh, wo+lh+rh, gran, rights, cmp)).fork();
+ nleft = lh;
+ nright = rh;
+ }
+
+ int l = lo;
+ int lFence = l + nleft;
+ int r = ro;
+ int rFence = r + nright;
+ int k = wo;
+ while (l < lFence && r < rFence) {
+ T al = a[l];
+ T ar = a[r];
+ T t;
+ if (cmp.compare(al, ar) <= 0) {
+ ++l;
+ t = al;
+ } else {
+ ++r;
+ t = ar;
+ }
+ w[k++] = t;
+ }
+ while (l < lFence)
+ w[k++] = a[l++];
+ while (r < rFence)
+ w[k++] = a[r++];
+ while (rights != null) {
+ if (rights.tryUnfork())
+ rights.compute();
+ else
+ rights.join();
+ rights = rights.next;
+ }
+ }
+ }
+ } // FJComparator
+
+ /** Utility class to sort half a partitioned array */
+ private static final class FJSubSorter extends RecursiveAction {
+ static final long serialVersionUID = 9159249695527935512L;
+ final RecursiveAction left;
+ final RecursiveAction right;
+ final RecursiveAction merger;
+
+ FJSubSorter(RecursiveAction left, RecursiveAction right,
+ RecursiveAction merger) {
+ this.left = left;
+ this.right = right;
+ this.merger = merger;
+ }
+
+ public void compute() {
+ right.fork();
+ left.invoke();
+ right.join();
+ merger.invoke();
+ }
+ }
+}
--- a/jdk/src/share/classes/java/util/Calendar.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/util/Calendar.java Fri Dec 28 18:36:41 2012 -0800
@@ -749,7 +749,7 @@
*
* @see #NARROW_STANDALONE
* @see #SHORT_FORMAT
- * @see #LONG_FOTMAT
+ * @see #LONG_FORMAT
* @since 1.8
*/
public static final int NARROW_FORMAT = 4;
--- a/jdk/src/share/classes/java/util/Properties.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/util/Properties.java Fri Dec 28 18:36:41 2012 -0800
@@ -1188,7 +1188,7 @@
provider = loadProviderAsService(cl);
if (provider != null)
return provider;
- throw new InternalError("No fallback");
+ return new jdk.internal.util.xml.BasicXmlPropertiesProvider();
}});
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/util/concurrent/CountedCompleter.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,743 @@
+/*
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+/**
+ * A {@link ForkJoinTask} with a completion action performed when
+ * triggered and there are no remaining pending
+ * actions. CountedCompleters are in general more robust in the
+ * presence of subtask stalls and blockage than are other forms of
+ * ForkJoinTasks, but are less intuitive to program. Uses of
+ * CountedCompleter are similar to those of other completion based
+ * components (such as {@link java.nio.channels.CompletionHandler})
+ * except that multiple <em>pending</em> completions may be necessary
+ * to trigger the completion action {@link #onCompletion}, not just one.
+ * Unless initialized otherwise, the {@linkplain #getPendingCount pending
+ * count} starts at zero, but may be (atomically) changed using
+ * methods {@link #setPendingCount}, {@link #addToPendingCount}, and
+ * {@link #compareAndSetPendingCount}. Upon invocation of {@link
+ * #tryComplete}, if the pending action count is nonzero, it is
+ * decremented; otherwise, the completion action is performed, and if
+ * this completer itself has a completer, the process is continued
+ * with its completer. As is the case with related synchronization
+ * components such as {@link java.util.concurrent.Phaser Phaser} and
+ * {@link java.util.concurrent.Semaphore Semaphore}, these methods
+ * affect only internal counts; they do not establish any further
+ * internal bookkeeping. In particular, the identities of pending
+ * tasks are not maintained. As illustrated below, you can create
+ * subclasses that do record some or all pending tasks or their
+ * results when needed. As illustrated below, utility methods
+ * supporting customization of completion traversals are also
+ * provided. However, because CountedCompleters provide only basic
+ * synchronization mechanisms, it may be useful to create further
+ * abstract subclasses that maintain linkages, fields, and additional
+ * support methods appropriate for a set of related usages.
+ *
+ * <p>A concrete CountedCompleter class must define method {@link
+ * #compute}, that should in most cases (as illustrated below), invoke
+ * {@code tryComplete()} once before returning. The class may also
+ * optionally override method {@link #onCompletion} to perform an
+ * action upon normal completion, and method {@link
+ * #onExceptionalCompletion} to perform an action upon any exception.
+ *
+ * <p>CountedCompleters most often do not bear results, in which case
+ * they are normally declared as {@code CountedCompleter<Void>}, and
+ * will always return {@code null} as a result value. In other cases,
+ * you should override method {@link #getRawResult} to provide a
+ * result from {@code join(), invoke()}, and related methods. In
+ * general, this method should return the value of a field (or a
+ * function of one or more fields) of the CountedCompleter object that
+ * holds the result upon completion. Method {@link #setRawResult} by
+ * default plays no role in CountedCompleters. It is possible, but
+ * rarely applicable, to override this method to maintain other
+ * objects or fields holding result data.
+ *
+ * <p>A CountedCompleter that does not itself have a completer (i.e.,
+ * one for which {@link #getCompleter} returns {@code null}) can be
+ * used as a regular ForkJoinTask with this added functionality.
+ * However, any completer that in turn has another completer serves
+ * only as an internal helper for other computations, so its own task
+ * status (as reported in methods such as {@link ForkJoinTask#isDone})
+ * is arbitrary; this status changes only upon explicit invocations of
+ * {@link #complete}, {@link ForkJoinTask#cancel}, {@link
+ * ForkJoinTask#completeExceptionally} or upon exceptional completion
+ * of method {@code compute}. Upon any exceptional completion, the
+ * exception may be relayed to a task's completer (and its completer,
+ * and so on), if one exists and it has not otherwise already
+ * completed. Similarly, cancelling an internal CountedCompleter has
+ * only a local effect on that completer, so is not often useful.
+ *
+ * <p><b>Sample Usages.</b>
+ *
+ * <p><b>Parallel recursive decomposition.</b> CountedCompleters may
+ * be arranged in trees similar to those often used with {@link
+ * RecursiveAction}s, although the constructions involved in setting
+ * them up typically vary. Here, the completer of each task is its
+ * parent in the computation tree. Even though they entail a bit more
+ * bookkeeping, CountedCompleters may be better choices when applying
+ * a possibly time-consuming operation (that cannot be further
+ * subdivided) to each element of an array or collection; especially
+ * when the operation takes a significantly different amount of time
+ * to complete for some elements than others, either because of
+ * intrinsic variation (for example I/O) or auxiliary effects such as
+ * garbage collection. Because CountedCompleters provide their own
+ * continuations, other threads need not block waiting to perform
+ * them.
+ *
+ * <p>For example, here is an initial version of a class that uses
+ * divide-by-two recursive decomposition to divide work into single
+ * pieces (leaf tasks). Even when work is split into individual calls,
+ * tree-based techniques are usually preferable to directly forking
+ * leaf tasks, because they reduce inter-thread communication and
+ * improve load balancing. In the recursive case, the second of each
+ * pair of subtasks to finish triggers completion of its parent
+ * (because no result combination is performed, the default no-op
+ * implementation of method {@code onCompletion} is not overridden). A
+ * static utility method sets up the base task and invokes it
+ * (here, implicitly using the {@link ForkJoinPool#commonPool()}).
+ *
+ * <pre> {@code
+ * class MyOperation<E> { void apply(E e) { ... } }
+ *
+ * class ForEach<E> extends CountedCompleter<Void> {
+ *
+ * public static <E> void forEach(E[] array, MyOperation<E> op) {
+ * new ForEach<E>(null, array, op, 0, array.length).invoke();
+ * }
+ *
+ * final E[] array; final MyOperation<E> op; final int lo, hi;
+ * ForEach(CountedCompleter<?> p, E[] array, MyOperation<E> op, int lo, int hi) {
+ * super(p);
+ * this.array = array; this.op = op; this.lo = lo; this.hi = hi;
+ * }
+ *
+ * public void compute() { // version 1
+ * if (hi - lo >= 2) {
+ * int mid = (lo + hi) >>> 1;
+ * setPendingCount(2); // must set pending count before fork
+ * new ForEach(this, array, op, mid, hi).fork(); // right child
+ * new ForEach(this, array, op, lo, mid).fork(); // left child
+ * }
+ * else if (hi > lo)
+ * op.apply(array[lo]);
+ * tryComplete();
+ * }
+ * }}</pre>
+ *
+ * This design can be improved by noticing that in the recursive case,
+ * the task has nothing to do after forking its right task, so can
+ * directly invoke its left task before returning. (This is an analog
+ * of tail recursion removal.) Also, because the task returns upon
+ * executing its left task (rather than falling through to invoke
+ * {@code tryComplete}) the pending count is set to one:
+ *
+ * <pre> {@code
+ * class ForEach<E> ...
+ * public void compute() { // version 2
+ * if (hi - lo >= 2) {
+ * int mid = (lo + hi) >>> 1;
+ * setPendingCount(1); // only one pending
+ * new ForEach(this, array, op, mid, hi).fork(); // right child
+ * new ForEach(this, array, op, lo, mid).compute(); // direct invoke
+ * }
+ * else {
+ * if (hi > lo)
+ * op.apply(array[lo]);
+ * tryComplete();
+ * }
+ * }
+ * }</pre>
+ *
+ * As a further improvement, notice that the left task need not even
+ * exist. Instead of creating a new one, we can iterate using the
+ * original task, and add a pending count for each fork. Additionally,
+ * because no task in this tree implements an {@link #onCompletion}
+ * method, {@code tryComplete()} can be replaced with {@link
+ * #propagateCompletion}.
+ *
+ * <pre> {@code
+ * class ForEach<E> ...
+ * public void compute() { // version 3
+ * int l = lo, h = hi;
+ * while (h - l >= 2) {
+ * int mid = (l + h) >>> 1;
+ * addToPendingCount(1);
+ * new ForEach(this, array, op, mid, h).fork(); // right child
+ * h = mid;
+ * }
+ * if (h > l)
+ * op.apply(array[l]);
+ * propagateCompletion();
+ * }
+ * }</pre>
+ *
+ * Additional improvements of such classes might entail precomputing
+ * pending counts so that they can be established in constructors,
+ * specializing classes for leaf steps, subdividing by say, four,
+ * instead of two per iteration, and using an adaptive threshold
+ * instead of always subdividing down to single elements.
+ *
+ * <p><b>Searching.</b> A tree of CountedCompleters can search for a
+ * value or property in different parts of a data structure, and
+ * report a result in an {@link
+ * java.util.concurrent.atomic.AtomicReference AtomicReference} as
+ * soon as one is found. The others can poll the result to avoid
+ * unnecessary work. (You could additionally {@linkplain #cancel
+ * cancel} other tasks, but it is usually simpler and more efficient
+ * to just let them notice that the result is set and if so skip
+ * further processing.) Illustrating again with an array using full
+ * partitioning (again, in practice, leaf tasks will almost always
+ * process more than one element):
+ *
+ * <pre> {@code
+ * class Searcher<E> extends CountedCompleter<E> {
+ * final E[] array; final AtomicReference<E> result; final int lo, hi;
+ * Searcher(CountedCompleter<?> p, E[] array, AtomicReference<E> result, int lo, int hi) {
+ * super(p);
+ * this.array = array; this.result = result; this.lo = lo; this.hi = hi;
+ * }
+ * public E getRawResult() { return result.get(); }
+ * public void compute() { // similar to ForEach version 3
+ * int l = lo, h = hi;
+ * while (result.get() == null && h >= l) {
+ * if (h - l >= 2) {
+ * int mid = (l + h) >>> 1;
+ * addToPendingCount(1);
+ * new Searcher(this, array, result, mid, h).fork();
+ * h = mid;
+ * }
+ * else {
+ * E x = array[l];
+ * if (matches(x) && result.compareAndSet(null, x))
+ * quietlyCompleteRoot(); // root task is now joinable
+ * break;
+ * }
+ * }
+ * tryComplete(); // normally complete whether or not found
+ * }
+ * boolean matches(E e) { ... } // return true if found
+ *
+ * public static <E> E search(E[] array) {
+ * return new Searcher<E>(null, array, new AtomicReference<E>(), 0, array.length).invoke();
+ * }
+ *}}</pre>
+ *
+ * In this example, as well as others in which tasks have no other
+ * effects except to compareAndSet a common result, the trailing
+ * unconditional invocation of {@code tryComplete} could be made
+ * conditional ({@code if (result.get() == null) tryComplete();})
+ * because no further bookkeeping is required to manage completions
+ * once the root task completes.
+ *
+ * <p><b>Recording subtasks.</b> CountedCompleter tasks that combine
+ * results of multiple subtasks usually need to access these results
+ * in method {@link #onCompletion}. As illustrated in the following
+ * class (that performs a simplified form of map-reduce where mappings
+ * and reductions are all of type {@code E}), one way to do this in
+ * divide and conquer designs is to have each subtask record its
+ * sibling, so that it can be accessed in method {@code onCompletion}.
+ * This technique applies to reductions in which the order of
+ * combining left and right results does not matter; ordered
+ * reductions require explicit left/right designations. Variants of
+ * other streamlinings seen in the above examples may also apply.
+ *
+ * <pre> {@code
+ * class MyMapper<E> { E apply(E v) { ... } }
+ * class MyReducer<E> { E apply(E x, E y) { ... } }
+ * class MapReducer<E> extends CountedCompleter<E> {
+ * final E[] array; final MyMapper<E> mapper;
+ * final MyReducer<E> reducer; final int lo, hi;
+ * MapReducer<E> sibling;
+ * E result;
+ * MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
+ * MyReducer<E> reducer, int lo, int hi) {
+ * super(p);
+ * this.array = array; this.mapper = mapper;
+ * this.reducer = reducer; this.lo = lo; this.hi = hi;
+ * }
+ * public void compute() {
+ * if (hi - lo >= 2) {
+ * int mid = (lo + hi) >>> 1;
+ * MapReducer<E> left = new MapReducer(this, array, mapper, reducer, lo, mid);
+ * MapReducer<E> right = new MapReducer(this, array, mapper, reducer, mid, hi);
+ * left.sibling = right;
+ * right.sibling = left;
+ * setPendingCount(1); // only right is pending
+ * right.fork();
+ * left.compute(); // directly execute left
+ * }
+ * else {
+ * if (hi > lo)
+ * result = mapper.apply(array[lo]);
+ * tryComplete();
+ * }
+ * }
+ * public void onCompletion(CountedCompleter<?> caller) {
+ * if (caller != this) {
+ * MapReducer<E> child = (MapReducer<E>)caller;
+ * MapReducer<E> sib = child.sibling;
+ * if (sib == null || sib.result == null)
+ * result = child.result;
+ * else
+ * result = reducer.apply(child.result, sib.result);
+ * }
+ * }
+ * public E getRawResult() { return result; }
+ *
+ * public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
+ * return new MapReducer<E>(null, array, mapper, reducer,
+ * 0, array.length).invoke();
+ * }
+ * }}</pre>
+ *
+ * Here, method {@code onCompletion} takes a form common to many
+ * completion designs that combine results. This callback-style method
+ * is triggered once per task, in either of the two different contexts
+ * in which the pending count is, or becomes, zero: (1) by a task
+ * itself, if its pending count is zero upon invocation of {@code
+ * tryComplete}, or (2) by any of its subtasks when they complete and
+ * decrement the pending count to zero. The {@code caller} argument
+ * distinguishes cases. Most often, when the caller is {@code this},
+ * no action is necessary. Otherwise the caller argument can be used
+ * (usually via a cast) to supply a value (and/or links to other
+ * values) to be combined. Assuming proper use of pending counts, the
+ * actions inside {@code onCompletion} occur (once) upon completion of
+ * a task and its subtasks. No additional synchronization is required
+ * within this method to ensure thread safety of accesses to fields of
+ * this task or other completed tasks.
+ *
+ * <p><b>Completion Traversals</b>. If using {@code onCompletion} to
+ * process completions is inapplicable or inconvenient, you can use
+ * methods {@link #firstComplete} and {@link #nextComplete} to create
+ * custom traversals. For example, to define a MapReducer that only
+ * splits out right-hand tasks in the form of the third ForEach
+ * example, the completions must cooperatively reduce along
+ * unexhausted subtask links, which can be done as follows:
+ *
+ * <pre> {@code
+ * class MapReducer<E> extends CountedCompleter<E> { // version 2
+ * final E[] array; final MyMapper<E> mapper;
+ * final MyReducer<E> reducer; final int lo, hi;
+ * MapReducer<E> forks, next; // record subtask forks in list
+ * E result;
+ * MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
+ * MyReducer<E> reducer, int lo, int hi, MapReducer<E> next) {
+ * super(p);
+ * this.array = array; this.mapper = mapper;
+ * this.reducer = reducer; this.lo = lo; this.hi = hi;
+ * this.next = next;
+ * }
+ * public void compute() {
+ * int l = lo, h = hi;
+ * while (h - l >= 2) {
+ * int mid = (l + h) >>> 1;
+ * addToPendingCount(1);
+ * (forks = new MapReducer(this, array, mapper, reducer, mid, h, forks)).fork;
+ * h = mid;
+ * }
+ * if (h > l)
+ * result = mapper.apply(array[l]);
+ * // process completions by reducing along and advancing subtask links
+ * for (CountedCompleter<?> c = firstComplete(); c != null; c = c.nextComplete()) {
+ * for (MapReducer t = (MapReducer)c, s = t.forks; s != null; s = t.forks = s.next)
+ * t.result = reducer.apply(t.result, s.result);
+ * }
+ * }
+ * public E getRawResult() { return result; }
+ *
+ * public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
+ * return new MapReducer<E>(null, array, mapper, reducer,
+ * 0, array.length, null).invoke();
+ * }
+ * }}</pre>
+ *
+ * <p><b>Triggers.</b> Some CountedCompleters are themselves never
+ * forked, but instead serve as bits of plumbing in other designs;
+ * including those in which the completion of one of more async tasks
+ * triggers another async task. For example:
+ *
+ * <pre> {@code
+ * class HeaderBuilder extends CountedCompleter<...> { ... }
+ * class BodyBuilder extends CountedCompleter<...> { ... }
+ * class PacketSender extends CountedCompleter<...> {
+ * PacketSender(...) { super(null, 1); ... } // trigger on second completion
+ * public void compute() { } // never called
+ * public void onCompletion(CountedCompleter<?> caller) { sendPacket(); }
+ * }
+ * // sample use:
+ * PacketSender p = new PacketSender();
+ * new HeaderBuilder(p, ...).fork();
+ * new BodyBuilder(p, ...).fork();
+ * }</pre>
+ *
+ * @since 1.8
+ * @author Doug Lea
+ */
+public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
+ private static final long serialVersionUID = 5232453752276485070L;
+
+ /** This task's completer, or null if none */
+ final CountedCompleter<?> completer;
+ /** The number of pending tasks until completion */
+ volatile int pending;
+
+ /**
+ * Creates a new CountedCompleter with the given completer
+ * and initial pending count.
+ *
+ * @param completer this task's completer, or {@code null} if none
+ * @param initialPendingCount the initial pending count
+ */
+ protected CountedCompleter(CountedCompleter<?> completer,
+ int initialPendingCount) {
+ this.completer = completer;
+ this.pending = initialPendingCount;
+ }
+
+ /**
+ * Creates a new CountedCompleter with the given completer
+ * and an initial pending count of zero.
+ *
+ * @param completer this task's completer, or {@code null} if none
+ */
+ protected CountedCompleter(CountedCompleter<?> completer) {
+ this.completer = completer;
+ }
+
+ /**
+ * Creates a new CountedCompleter with no completer
+ * and an initial pending count of zero.
+ */
+ protected CountedCompleter() {
+ this.completer = null;
+ }
+
+ /**
+ * The main computation performed by this task.
+ */
+ public abstract void compute();
+
+ /**
+ * Performs an action when method {@link #tryComplete} is invoked
+ * and the pending count is zero, or when the unconditional
+ * method {@link #complete} is invoked. By default, this method
+ * does nothing. You can distinguish cases by checking the
+ * identity of the given caller argument. If not equal to {@code
+ * this}, then it is typically a subtask that may contain results
+ * (and/or links to other results) to combine.
+ *
+ * @param caller the task invoking this method (which may
+ * be this task itself).
+ */
+ public void onCompletion(CountedCompleter<?> caller) {
+ }
+
+ /**
+ * Performs an action when method {@link #completeExceptionally}
+ * is invoked or method {@link #compute} throws an exception, and
+ * this task has not otherwise already completed normally. On
+ * entry to this method, this task {@link
+ * ForkJoinTask#isCompletedAbnormally}. The return value of this
+ * method controls further propagation: If {@code true} and this
+ * task has a completer, then this completer is also completed
+ * exceptionally. The default implementation of this method does
+ * nothing except return {@code true}.
+ *
+ * @param ex the exception
+ * @param caller the task invoking this method (which may
+ * be this task itself).
+ * @return true if this exception should be propagated to this
+ * task's completer, if one exists.
+ */
+ public boolean onExceptionalCompletion(Throwable ex, CountedCompleter<?> caller) {
+ return true;
+ }
+
+ /**
+ * Returns the completer established in this task's constructor,
+ * or {@code null} if none.
+ *
+ * @return the completer
+ */
+ public final CountedCompleter<?> getCompleter() {
+ return completer;
+ }
+
+ /**
+ * Returns the current pending count.
+ *
+ * @return the current pending count
+ */
+ public final int getPendingCount() {
+ return pending;
+ }
+
+ /**
+ * Sets the pending count to the given value.
+ *
+ * @param count the count
+ */
+ public final void setPendingCount(int count) {
+ pending = count;
+ }
+
+ /**
+ * Adds (atomically) the given value to the pending count.
+ *
+ * @param delta the value to add
+ */
+ public final void addToPendingCount(int delta) {
+ int c; // note: can replace with intrinsic in jdk8
+ do {} while (!U.compareAndSwapInt(this, PENDING, c = pending, c+delta));
+ }
+
+ /**
+ * Sets (atomically) the pending count to the given count only if
+ * it currently holds the given expected value.
+ *
+ * @param expected the expected value
+ * @param count the new value
+ * @return true if successful
+ */
+ public final boolean compareAndSetPendingCount(int expected, int count) {
+ return U.compareAndSwapInt(this, PENDING, expected, count);
+ }
+
+ /**
+ * If the pending count is nonzero, (atomically) decrements it.
+ *
+ * @return the initial (undecremented) pending count holding on entry
+ * to this method
+ */
+ public final int decrementPendingCountUnlessZero() {
+ int c;
+ do {} while ((c = pending) != 0 &&
+ !U.compareAndSwapInt(this, PENDING, c, c - 1));
+ return c;
+ }
+
+ /**
+ * Returns the root of the current computation; i.e., this
+ * task if it has no completer, else its completer's root.
+ *
+ * @return the root of the current computation
+ */
+ public final CountedCompleter<?> getRoot() {
+ CountedCompleter<?> a = this, p;
+ while ((p = a.completer) != null)
+ a = p;
+ return a;
+ }
+
+ /**
+ * If the pending count is nonzero, decrements the count;
+ * otherwise invokes {@link #onCompletion} and then similarly
+ * tries to complete this task's completer, if one exists,
+ * else marks this task as complete.
+ */
+ public final void tryComplete() {
+ CountedCompleter<?> a = this, s = a;
+ for (int c;;) {
+ if ((c = a.pending) == 0) {
+ a.onCompletion(s);
+ if ((a = (s = a).completer) == null) {
+ s.quietlyComplete();
+ return;
+ }
+ }
+ else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
+ return;
+ }
+ }
+
+ /**
+ * Equivalent to {@link #tryComplete} but does not invoke {@link
+ * #onCompletion} along the completion path: If the pending count
+ * is nonzero, decrements the count; otherwise, similarly tries to
+ * complete this task's completer, if one exists, else marks this
+ * task as complete. This method may be useful in cases where
+ * {@code onCompletion} should not, or need not, be invoked for
+ * each completer in a computation.
+ */
+ public final void propagateCompletion() {
+ CountedCompleter<?> a = this, s = a;
+ for (int c;;) {
+ if ((c = a.pending) == 0) {
+ if ((a = (s = a).completer) == null) {
+ s.quietlyComplete();
+ return;
+ }
+ }
+ else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
+ return;
+ }
+ }
+
+ /**
+ * Regardless of pending count, invokes {@link #onCompletion},
+ * marks this task as complete and further triggers {@link
+ * #tryComplete} on this task's completer, if one exists. The
+ * given rawResult is used as an argument to {@link #setRawResult}
+ * before invoking {@link #onCompletion} or marking this task as
+ * complete; its value is meaningful only for classes overriding
+ * {@code setRawResult}.
+ *
+ * <p>This method may be useful when forcing completion as soon as
+ * any one (versus all) of several subtask results are obtained.
+ * However, in the common (and recommended) case in which {@code
+ * setRawResult} is not overridden, this effect can be obtained
+ * more simply using {@code quietlyCompleteRoot();}.
+ *
+ * @param rawResult the raw result
+ */
+ public void complete(T rawResult) {
+ CountedCompleter<?> p;
+ setRawResult(rawResult);
+ onCompletion(this);
+ quietlyComplete();
+ if ((p = completer) != null)
+ p.tryComplete();
+ }
+
+
+ /**
+ * If this task's pending count is zero, returns this task;
+ * otherwise decrements its pending count and returns {@code
+ * null}. This method is designed to be used with {@link
+ * #nextComplete} in completion traversal loops.
+ *
+ * @return this task, if pending count was zero, else {@code null}
+ */
+ public final CountedCompleter<?> firstComplete() {
+ for (int c;;) {
+ if ((c = pending) == 0)
+ return this;
+ else if (U.compareAndSwapInt(this, PENDING, c, c - 1))
+ return null;
+ }
+ }
+
+ /**
+ * If this task does not have a completer, invokes {@link
+ * ForkJoinTask#quietlyComplete} and returns {@code null}. Or, if
+ * this task's pending count is non-zero, decrements its pending
+ * count and returns {@code null}. Otherwise, returns the
+ * completer. This method can be used as part of a completion
+ * traversal loop for homogeneous task hierarchies:
+ *
+ * <pre> {@code
+ * for (CountedCompleter<?> c = firstComplete();
+ * c != null;
+ * c = c.nextComplete()) {
+ * // ... process c ...
+ * }}</pre>
+ *
+ * @return the completer, or {@code null} if none
+ */
+ public final CountedCompleter<?> nextComplete() {
+ CountedCompleter<?> p;
+ if ((p = completer) != null)
+ return p.firstComplete();
+ else {
+ quietlyComplete();
+ return null;
+ }
+ }
+
+ /**
+ * Equivalent to {@code getRoot().quietlyComplete()}.
+ */
+ public final void quietlyCompleteRoot() {
+ for (CountedCompleter<?> a = this, p;;) {
+ if ((p = a.completer) == null) {
+ a.quietlyComplete();
+ return;
+ }
+ a = p;
+ }
+ }
+
+ /**
+ * Supports ForkJoinTask exception propagation.
+ */
+ void internalPropagateException(Throwable ex) {
+ CountedCompleter<?> a = this, s = a;
+ while (a.onExceptionalCompletion(ex, s) &&
+ (a = (s = a).completer) != null && a.status >= 0)
+ a.recordExceptionalCompletion(ex);
+ }
+
+ /**
+ * Implements execution conventions for CountedCompleters.
+ */
+ protected final boolean exec() {
+ compute();
+ return false;
+ }
+
+ /**
+ * Returns the result of the computation. By default
+ * returns {@code null}, which is appropriate for {@code Void}
+ * actions, but in other cases should be overridden, almost
+ * always to return a field or function of a field that
+ * holds the result upon completion.
+ *
+ * @return the result of the computation
+ */
+ public T getRawResult() { return null; }
+
+ /**
+ * A method that result-bearing CountedCompleters may optionally
+ * use to help maintain result data. By default, does nothing.
+ * Overrides are not recommended. However, if this method is
+ * overridden to update existing objects or fields, then it must
+ * in general be defined to be thread-safe.
+ */
+ protected void setRawResult(T t) { }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U;
+ private static final long PENDING;
+ static {
+ try {
+ U = sun.misc.Unsafe.getUnsafe();
+ PENDING = U.objectFieldOffset
+ (CountedCompleter.class.getDeclaredField("pending"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+}
--- a/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java Fri Dec 28 18:36:41 2012 -0800
@@ -40,7 +40,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.Random;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
@@ -48,11 +47,6 @@
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.LockSupport;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.locks.Condition;
/**
* An {@link ExecutorService} for running {@link ForkJoinTask}s.
@@ -63,21 +57,31 @@
* <p>A {@code ForkJoinPool} differs from other kinds of {@link
* ExecutorService} mainly by virtue of employing
* <em>work-stealing</em>: all threads in the pool attempt to find and
- * execute subtasks created by other active tasks (eventually blocking
- * waiting for work if none exist). This enables efficient processing
- * when most tasks spawn other subtasks (as do most {@code
- * ForkJoinTask}s). When setting <em>asyncMode</em> to true in
- * constructors, {@code ForkJoinPool}s may also be appropriate for use
- * with event-style tasks that are never joined.
+ * execute tasks submitted to the pool and/or created by other active
+ * tasks (eventually blocking waiting for work if none exist). This
+ * enables efficient processing when most tasks spawn other subtasks
+ * (as do most {@code ForkJoinTask}s), as well as when many small
+ * tasks are submitted to the pool from external clients. Especially
+ * when setting <em>asyncMode</em> to true in constructors, {@code
+ * ForkJoinPool}s may also be appropriate for use with event-style
+ * tasks that are never joined.
*
- * <p>A {@code ForkJoinPool} is constructed with a given target
- * parallelism level; by default, equal to the number of available
- * processors. The pool attempts to maintain enough active (or
- * available) threads by dynamically adding, suspending, or resuming
- * internal worker threads, even if some tasks are stalled waiting to
- * join others. However, no such adjustments are guaranteed in the
- * face of blocked IO or other unmanaged synchronization. The nested
- * {@link ManagedBlocker} interface enables extension of the kinds of
+ * <p>A static {@link #commonPool()} is available and appropriate for
+ * most applications. The common pool is used by any ForkJoinTask that
+ * is not explicitly submitted to a specified pool. Using the common
+ * pool normally reduces resource usage (its threads are slowly
+ * reclaimed during periods of non-use, and reinstated upon subsequent
+ * use).
+ *
+ * <p>For applications that require separate or custom pools, a {@code
+ * ForkJoinPool} may be constructed with a given target parallelism
+ * level; by default, equal to the number of available processors. The
+ * pool attempts to maintain enough active (or available) threads by
+ * dynamically adding, suspending, or resuming internal worker
+ * threads, even if some tasks are stalled waiting to join
+ * others. However, no such adjustments are guaranteed in the face of
+ * blocked I/O or other unmanaged synchronization. The nested {@link
+ * ManagedBlocker} interface enables extension of the kinds of
* synchronization accommodated.
*
* <p>In addition to execution and lifecycle control methods, this
@@ -87,16 +91,17 @@
* {@link #toString} returns indications of pool state in a
* convenient form for informal monitoring.
*
- * <p> As is the case with other ExecutorServices, there are three
- * main task execution methods summarized in the following
- * table. These are designed to be used by clients not already engaged
- * in fork/join computations in the current pool. The main forms of
- * these methods accept instances of {@code ForkJoinTask}, but
- * overloaded forms also allow mixed execution of plain {@code
+ * <p>As is the case with other ExecutorServices, there are three
+ * main task execution methods summarized in the following table.
+ * These are designed to be used primarily by clients not already
+ * engaged in fork/join computations in the current pool. The main
+ * forms of these methods accept instances of {@code ForkJoinTask},
+ * but overloaded forms also allow mixed execution of plain {@code
* Runnable}- or {@code Callable}- based activities as well. However,
- * tasks that are already executing in a pool should normally
- * <em>NOT</em> use these pool execution methods, but instead use the
- * within-computation forms listed in the table.
+ * tasks that are already executing in a pool should normally instead
+ * use the within-computation forms listed in the table unless using
+ * async event-style tasks that are not usually joined, in which case
+ * there is little difference among choice of methods.
*
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <tr>
@@ -121,23 +126,16 @@
* </tr>
* </table>
*
- * <p><b>Sample Usage.</b> Normally a single {@code ForkJoinPool} is
- * used for all parallel task execution in a program or subsystem.
- * Otherwise, use would not usually outweigh the construction and
- * bookkeeping overhead of creating a large set of threads. For
- * example, a common pool could be used for the {@code SortTasks}
- * illustrated in {@link RecursiveAction}. Because {@code
- * ForkJoinPool} uses threads in {@linkplain java.lang.Thread#isDaemon
- * daemon} mode, there is typically no need to explicitly {@link
- * #shutdown} such a pool upon program exit.
- *
- * <pre>
- * static final ForkJoinPool mainPool = new ForkJoinPool();
- * ...
- * public void sort(long[] array) {
- * mainPool.invoke(new SortTask(array, 0, array.length));
- * }
- * </pre>
+ * <p>The common pool is by default constructed with default
+ * parameters, but these may be controlled by setting three {@link
+ * System#getProperty system properties} with prefix {@code
+ * java.util.concurrent.ForkJoinPool.common}: {@code parallelism} --
+ * an integer greater than zero, {@code threadFactory} -- the class
+ * name of a {@link ForkJoinWorkerThreadFactory}, and {@code
+ * exceptionHandler} -- the class name of a {@link
+ * java.lang.Thread.UncaughtExceptionHandler
+ * Thread.UncaughtExceptionHandler}. Upon any error in establishing
+ * these settings, default parameters are used.
*
* <p><b>Implementation notes</b>: This implementation restricts the
* maximum number of running threads to 32767. Attempts to create
@@ -156,214 +154,388 @@
/*
* Implementation Overview
*
- * This class provides the central bookkeeping and control for a
- * set of worker threads: Submissions from non-FJ threads enter
- * into a submission queue. Workers take these tasks and typically
- * split them into subtasks that may be stolen by other workers.
- * Preference rules give first priority to processing tasks from
- * their own queues (LIFO or FIFO, depending on mode), then to
- * randomized FIFO steals of tasks in other worker queues, and
- * lastly to new submissions.
+ * This class and its nested classes provide the main
+ * functionality and control for a set of worker threads:
+ * Submissions from non-FJ threads enter into submission queues.
+ * Workers take these tasks and typically split them into subtasks
+ * that may be stolen by other workers. Preference rules give
+ * first priority to processing tasks from their own queues (LIFO
+ * or FIFO, depending on mode), then to randomized FIFO steals of
+ * tasks in other queues.
+ *
+ * WorkQueues
+ * ==========
+ *
+ * Most operations occur within work-stealing queues (in nested
+ * class WorkQueue). These are special forms of Deques that
+ * support only three of the four possible end-operations -- push,
+ * pop, and poll (aka steal), under the further constraints that
+ * push and pop are called only from the owning thread (or, as
+ * extended here, under a lock), while poll may be called from
+ * other threads. (If you are unfamiliar with them, you probably
+ * want to read Herlihy and Shavit's book "The Art of
+ * Multiprocessor programming", chapter 16 describing these in
+ * more detail before proceeding.) The main work-stealing queue
+ * design is roughly similar to those in the papers "Dynamic
+ * Circular Work-Stealing Deque" by Chase and Lev, SPAA 2005
+ * (http://research.sun.com/scalable/pubs/index.html) and
+ * "Idempotent work stealing" by Michael, Saraswat, and Vechev,
+ * PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186).
+ * The main differences ultimately stem from GC requirements that
+ * we null out taken slots as soon as we can, to maintain as small
+ * a footprint as possible even in programs generating huge
+ * numbers of tasks. To accomplish this, we shift the CAS
+ * arbitrating pop vs poll (steal) from being on the indices
+ * ("base" and "top") to the slots themselves. So, both a
+ * successful pop and poll mainly entail a CAS of a slot from
+ * non-null to null. Because we rely on CASes of references, we
+ * do not need tag bits on base or top. They are simple ints as
+ * used in any circular array-based queue (see for example
+ * ArrayDeque). Updates to the indices must still be ordered in a
+ * way that guarantees that top == base means the queue is empty,
+ * but otherwise may err on the side of possibly making the queue
+ * appear nonempty when a push, pop, or poll have not fully
+ * committed. Note that this means that the poll operation,
+ * considered individually, is not wait-free. One thief cannot
+ * successfully continue until another in-progress one (or, if
+ * previously empty, a push) completes. However, in the
+ * aggregate, we ensure at least probabilistic non-blockingness.
+ * If an attempted steal fails, a thief always chooses a different
+ * random victim target to try next. So, in order for one thief to
+ * progress, it suffices for any in-progress poll or new push on
+ * any empty queue to complete. (This is why we normally use
+ * method pollAt and its variants that try once at the apparent
+ * base index, else consider alternative actions, rather than
+ * method poll.)
+ *
+ * This approach also enables support of a user mode in which local
+ * task processing is in FIFO, not LIFO order, simply by using
+ * poll rather than pop. This can be useful in message-passing
+ * frameworks in which tasks are never joined. However neither
+ * mode considers affinities, loads, cache localities, etc, so
+ * rarely provide the best possible performance on a given
+ * machine, but portably provide good throughput by averaging over
+ * these factors. (Further, even if we did try to use such
+ * information, we do not usually have a basis for exploiting it.
+ * For example, some sets of tasks profit from cache affinities,
+ * but others are harmed by cache pollution effects.)
+ *
+ * WorkQueues are also used in a similar way for tasks submitted
+ * to the pool. We cannot mix these tasks in the same queues used
+ * for work-stealing (this would contaminate lifo/fifo
+ * processing). Instead, we randomly associate submission queues
+ * with submitting threads, using a form of hashing. The
+ * ThreadLocal Submitter class contains a value initially used as
+ * a hash code for choosing existing queues, but may be randomly
+ * repositioned upon contention with other submitters. In
+ * essence, submitters act like workers except that they are
+ * restricted to executing local tasks that they submitted (or in
+ * the case of CountedCompleters, others with the same root task).
+ * However, because most shared/external queue operations are more
+ * expensive than internal, and because, at steady state, external
+ * submitters will compete for CPU with workers, ForkJoinTask.join
+ * and related methods disable them from repeatedly helping to
+ * process tasks if all workers are active. Insertion of tasks in
+ * shared mode requires a lock (mainly to protect in the case of
+ * resizing) but we use only a simple spinlock (using bits in
+ * field qlock), because submitters encountering a busy queue move
+ * on to try or create other queues -- they block only when
+ * creating and registering new queues.
+ *
+ * Management
+ * ==========
*
* The main throughput advantages of work-stealing stem from
* decentralized control -- workers mostly take tasks from
* themselves or each other. We cannot negate this in the
* implementation of other management responsibilities. The main
* tactic for avoiding bottlenecks is packing nearly all
- * essentially atomic control state into a single 64bit volatile
- * variable ("ctl"). This variable is read on the order of 10-100
- * times as often as it is modified (always via CAS). (There is
- * some additional control state, for example variable "shutdown"
- * for which we can cope with uncoordinated updates.) This
- * streamlines synchronization and control at the expense of messy
- * constructions needed to repack status bits upon updates.
- * Updates tend not to contend with each other except during
- * bursts while submitted tasks begin or end. In some cases when
- * they do contend, threads can instead do something else
- * (usually, scan for tasks) until contention subsides.
+ * essentially atomic control state into two volatile variables
+ * that are by far most often read (not written) as status and
+ * consistency checks.
*
- * To enable packing, we restrict maximum parallelism to (1<<15)-1
- * (which is far in excess of normal operating range) to allow
- * ids, counts, and their negations (used for thresholding) to fit
- * into 16bit fields.
+ * Field "ctl" contains 64 bits holding all the information needed
+ * to atomically decide to add, inactivate, enqueue (on an event
+ * queue), dequeue, and/or re-activate workers. To enable this
+ * packing, we restrict maximum parallelism to (1<<15)-1 (which is
+ * far in excess of normal operating range) to allow ids, counts,
+ * and their negations (used for thresholding) to fit into 16bit
+ * fields.
+ *
+ * Field "plock" is a form of sequence lock with a saturating
+ * shutdown bit (similarly for per-queue "qlocks"), mainly
+ * protecting updates to the workQueues array, as well as to
+ * enable shutdown. When used as a lock, it is normally only very
+ * briefly held, so is nearly always available after at most a
+ * brief spin, but we use a monitor-based backup strategy to
+ * block when needed.
*
- * Recording Workers. Workers are recorded in the "workers" array
- * that is created upon pool construction and expanded if (rarely)
- * necessary. This is an array as opposed to some other data
- * structure to support index-based random steals by workers.
- * Updates to the array recording new workers and unrecording
- * terminated ones are protected from each other by a seqLock
- * (scanGuard) but the array is otherwise concurrently readable,
- * and accessed directly by workers. To simplify index-based
- * operations, the array size is always a power of two, and all
- * readers must tolerate null slots. To avoid flailing during
- * start-up, the array is presized to hold twice #parallelism
- * workers (which is unlikely to need further resizing during
- * execution). But to avoid dealing with so many null slots,
- * variable scanGuard includes a mask for the nearest power of two
- * that contains all current workers. All worker thread creation
- * is on-demand, triggered by task submissions, replacement of
- * terminated workers, and/or compensation for blocked
- * workers. However, all other support code is set up to work with
- * other policies. To ensure that we do not hold on to worker
- * references that would prevent GC, ALL accesses to workers are
- * via indices into the workers array (which is one source of some
- * of the messy code constructions here). In essence, the workers
- * array serves as a weak reference mechanism. Thus for example
- * the wait queue field of ctl stores worker indices, not worker
- * references. Access to the workers in associated methods (for
- * example signalWork) must both index-check and null-check the
- * IDs. All such accesses ignore bad IDs by returning out early
- * from what they are doing, since this can only be associated
- * with termination, in which case it is OK to give up.
+ * Recording WorkQueues. WorkQueues are recorded in the
+ * "workQueues" array that is created upon first use and expanded
+ * if necessary. Updates to the array while recording new workers
+ * and unrecording terminated ones are protected from each other
+ * by a lock but the array is otherwise concurrently readable, and
+ * accessed directly. To simplify index-based operations, the
+ * array size is always a power of two, and all readers must
+ * tolerate null slots. Worker queues are at odd indices. Shared
+ * (submission) queues are at even indices, up to a maximum of 64
+ * slots, to limit growth even if array needs to expand to add
+ * more workers. Grouping them together in this way simplifies and
+ * speeds up task scanning.
*
- * All uses of the workers array, as well as queue arrays, check
- * that the array is non-null (even if previously non-null). This
- * allows nulling during termination, which is currently not
- * necessary, but remains an option for resource-revocation-based
- * shutdown schemes.
+ * All worker thread creation is on-demand, triggered by task
+ * submissions, replacement of terminated workers, and/or
+ * compensation for blocked workers. However, all other support
+ * code is set up to work with other policies. To ensure that we
+ * do not hold on to worker references that would prevent GC, ALL
+ * accesses to workQueues are via indices into the workQueues
+ * array (which is one source of some of the messy code
+ * constructions here). In essence, the workQueues array serves as
+ * a weak reference mechanism. Thus for example the wait queue
+ * field of ctl stores indices, not references. Access to the
+ * workQueues in associated methods (for example signalWork) must
+ * both index-check and null-check the IDs. All such accesses
+ * ignore bad IDs by returning out early from what they are doing,
+ * since this can only be associated with termination, in which
+ * case it is OK to give up. All uses of the workQueues array
+ * also check that it is non-null (even if previously
+ * non-null). This allows nulling during termination, which is
+ * currently not necessary, but remains an option for
+ * resource-revocation-based shutdown schemes. It also helps
+ * reduce JIT issuance of uncommon-trap code, which tends to
+ * unnecessarily complicate control flow in some methods.
*
- * Wait Queuing. Unlike HPC work-stealing frameworks, we cannot
+ * Event Queuing. Unlike HPC work-stealing frameworks, we cannot
* let workers spin indefinitely scanning for tasks when none can
* be found immediately, and we cannot start/resume workers unless
* there appear to be tasks available. On the other hand, we must
* quickly prod them into action when new tasks are submitted or
- * generated. We park/unpark workers after placing in an event
- * wait queue when they cannot find work. This "queue" is actually
- * a simple Treiber stack, headed by the "id" field of ctl, plus a
- * 15bit counter value to both wake up waiters (by advancing their
- * count) and avoid ABA effects. Successors are held in worker
- * field "nextWait". Queuing deals with several intrinsic races,
- * mainly that a task-producing thread can miss seeing (and
+ * generated. In many usages, ramp-up time to activate workers is
+ * the main limiting factor in overall performance (this is
+ * compounded at program start-up by JIT compilation and
+ * allocation). So we try to streamline this as much as possible.
+ * We park/unpark workers after placing in an event wait queue
+ * when they cannot find work. This "queue" is actually a simple
+ * Treiber stack, headed by the "id" field of ctl, plus a 15bit
+ * counter value (that reflects the number of times a worker has
+ * been inactivated) to avoid ABA effects (we need only as many
+ * version numbers as worker threads). Successors are held in
+ * field WorkQueue.nextWait. Queuing deals with several intrinsic
+ * races, mainly that a task-producing thread can miss seeing (and
* signalling) another thread that gave up looking for work but
* has not yet entered the wait queue. We solve this by requiring
- * a full sweep of all workers both before (in scan()) and after
- * (in tryAwaitWork()) a newly waiting worker is added to the wait
- * queue. During a rescan, the worker might release some other
- * queued worker rather than itself, which has the same net
- * effect. Because enqueued workers may actually be rescanning
- * rather than waiting, we set and clear the "parked" field of
- * ForkJoinWorkerThread to reduce unnecessary calls to unpark.
- * (Use of the parked field requires a secondary recheck to avoid
- * missed signals.)
+ * a full sweep of all workers (via repeated calls to method
+ * scan()) both before and after a newly waiting worker is added
+ * to the wait queue. During a rescan, the worker might release
+ * some other queued worker rather than itself, which has the same
+ * net effect. Because enqueued workers may actually be rescanning
+ * rather than waiting, we set and clear the "parker" field of
+ * WorkQueues to reduce unnecessary calls to unpark. (This
+ * requires a secondary recheck to avoid missed signals.) Note
+ * the unusual conventions about Thread.interrupts surrounding
+ * parking and other blocking: Because interrupts are used solely
+ * to alert threads to check termination, which is checked anyway
+ * upon blocking, we clear status (using Thread.interrupted)
+ * before any call to park, so that park does not immediately
+ * return due to status being set via some other unrelated call to
+ * interrupt in user code.
*
* Signalling. We create or wake up workers only when there
* appears to be at least one task they might be able to find and
- * execute. When a submission is added or another worker adds a
- * task to a queue that previously had two or fewer tasks, they
- * signal waiting workers (or trigger creation of new ones if
- * fewer than the given parallelism level -- see signalWork).
- * These primary signals are buttressed by signals during rescans
- * as well as those performed when a worker steals a task and
- * notices that there are more tasks too; together these cover the
- * signals needed in cases when more than two tasks are pushed
- * but untaken.
+ * execute. However, many other threads may notice the same task
+ * and each signal to wake up a thread that might take it. So in
+ * general, pools will be over-signalled. When a submission is
+ * added or another worker adds a task to a queue that has fewer
+ * than two tasks, they signal waiting workers (or trigger
+ * creation of new ones if fewer than the given parallelism level
+ * -- signalWork), and may leave a hint to the unparked worker to
+ * help signal others upon wakeup). These primary signals are
+ * buttressed by others (see method helpSignal) whenever other
+ * threads scan for work or do not have a task to process. On
+ * most platforms, signalling (unpark) overhead time is noticeably
+ * long, and the time between signalling a thread and it actually
+ * making progress can be very noticeably long, so it is worth
+ * offloading these delays from critical paths as much as
+ * possible.
*
* Trimming workers. To release resources after periods of lack of
* use, a worker starting to wait when the pool is quiescent will
- * time out and terminate if the pool has remained quiescent for
- * SHRINK_RATE nanosecs. This will slowly propagate, eventually
- * terminating all workers after long periods of non-use.
- *
- * Submissions. External submissions are maintained in an
- * array-based queue that is structured identically to
- * ForkJoinWorkerThread queues except for the use of
- * submissionLock in method addSubmission. Unlike the case for
- * worker queues, multiple external threads can add new
- * submissions, so adding requires a lock.
+ * time out and terminate if the pool has remained quiescent for a
+ * given period -- a short period if there are more threads than
+ * parallelism, longer as the number of threads decreases. This
+ * will slowly propagate, eventually terminating all workers after
+ * periods of non-use.
*
- * Compensation. Beyond work-stealing support and lifecycle
- * control, the main responsibility of this framework is to take
- * actions when one worker is waiting to join a task stolen (or
- * always held by) another. Because we are multiplexing many
- * tasks on to a pool of workers, we can't just let them block (as
- * in Thread.join). We also cannot just reassign the joiner's
- * run-time stack with another and replace it later, which would
- * be a form of "continuation", that even if possible is not
- * necessarily a good idea since we sometimes need both an
- * unblocked task and its continuation to progress. Instead we
- * combine two tactics:
+ * Shutdown and Termination. A call to shutdownNow atomically sets
+ * a plock bit and then (non-atomically) sets each worker's
+ * qlock status, cancels all unprocessed tasks, and wakes up
+ * all waiting workers. Detecting whether termination should
+ * commence after a non-abrupt shutdown() call requires more work
+ * and bookkeeping. We need consensus about quiescence (i.e., that
+ * there is no more work). The active count provides a primary
+ * indication but non-abrupt shutdown still requires a rechecking
+ * scan for any workers that are inactive but not queued.
+ *
+ * Joining Tasks
+ * =============
+ *
+ * Any of several actions may be taken when one worker is waiting
+ * to join a task stolen (or always held) by another. Because we
+ * are multiplexing many tasks on to a pool of workers, we can't
+ * just let them block (as in Thread.join). We also cannot just
+ * reassign the joiner's run-time stack with another and replace
+ * it later, which would be a form of "continuation", that even if
+ * possible is not necessarily a good idea since we sometimes need
+ * both an unblocked task and its continuation to progress.
+ * Instead we combine two tactics:
*
* Helping: Arranging for the joiner to execute some task that it
- * would be running if the steal had not occurred. Method
- * ForkJoinWorkerThread.joinTask tracks joining->stealing
- * links to try to find such a task.
+ * would be running if the steal had not occurred.
*
* Compensating: Unless there are already enough live threads,
- * method tryPreBlock() may create or re-activate a spare
- * thread to compensate for blocked joiners until they
- * unblock.
+ * method tryCompensate() may create or re-activate a spare
+ * thread to compensate for blocked joiners until they unblock.
+ *
+ * A third form (implemented in tryRemoveAndExec) amounts to
+ * helping a hypothetical compensator: If we can readily tell that
+ * a possible action of a compensator is to steal and execute the
+ * task being joined, the joining thread can do so directly,
+ * without the need for a compensation thread (although at the
+ * expense of larger run-time stacks, but the tradeoff is
+ * typically worthwhile).
*
* The ManagedBlocker extension API can't use helping so relies
* only on compensation in method awaitBlocker.
*
+ * The algorithm in tryHelpStealer entails a form of "linear"
+ * helping: Each worker records (in field currentSteal) the most
+ * recent task it stole from some other worker. Plus, it records
+ * (in field currentJoin) the task it is currently actively
+ * joining. Method tryHelpStealer uses these markers to try to
+ * find a worker to help (i.e., steal back a task from and execute
+ * it) that could hasten completion of the actively joined task.
+ * In essence, the joiner executes a task that would be on its own
+ * local deque had the to-be-joined task not been stolen. This may
+ * be seen as a conservative variant of the approach in Wagner &
+ * Calder "Leapfrogging: a portable technique for implementing
+ * efficient futures" SIGPLAN Notices, 1993
+ * (http://portal.acm.org/citation.cfm?id=155354). It differs in
+ * that: (1) We only maintain dependency links across workers upon
+ * steals, rather than use per-task bookkeeping. This sometimes
+ * requires a linear scan of workQueues array to locate stealers,
+ * but often doesn't because stealers leave hints (that may become
+ * stale/wrong) of where to locate them. It is only a hint
+ * because a worker might have had multiple steals and the hint
+ * records only one of them (usually the most current). Hinting
+ * isolates cost to when it is needed, rather than adding to
+ * per-task overhead. (2) It is "shallow", ignoring nesting and
+ * potentially cyclic mutual steals. (3) It is intentionally
+ * racy: field currentJoin is updated only while actively joining,
+ * which means that we miss links in the chain during long-lived
+ * tasks, GC stalls etc (which is OK since blocking in such cases
+ * is usually a good idea). (4) We bound the number of attempts
+ * to find work (see MAX_HELP) and fall back to suspending the
+ * worker and if necessary replacing it with another.
+ *
+ * Helping actions for CountedCompleters are much simpler: Method
+ * helpComplete can take and execute any task with the same root
+ * as the task being waited on. However, this still entails some
+ * traversal of completer chains, so is less efficient than using
+ * CountedCompleters without explicit joins.
+ *
* It is impossible to keep exactly the target parallelism number
* of threads running at any given time. Determining the
* existence of conservatively safe helping targets, the
* availability of already-created spares, and the apparent need
- * to create new spares are all racy and require heuristic
- * guidance, so we rely on multiple retries of each. Currently,
- * in keeping with on-demand signalling policy, we compensate only
- * if blocking would leave less than one active (non-waiting,
- * non-blocked) worker. Additionally, to avoid some false alarms
- * due to GC, lagging counters, system activity, etc, compensated
- * blocking for joins is only attempted after rechecks stabilize
- * (retries are interspersed with Thread.yield, for good
- * citizenship). The variable blockedCount, incremented before
- * blocking and decremented after, is sometimes needed to
- * distinguish cases of waiting for work vs blocking on joins or
- * other managed sync. Both cases are equivalent for most pool
- * control, so we can update non-atomically. (Additionally,
- * contention on blockedCount alleviates some contention on ctl).
+ * to create new spares are all racy, so we rely on multiple
+ * retries of each. Compensation in the apparent absence of
+ * helping opportunities is challenging to control on JVMs, where
+ * GC and other activities can stall progress of tasks that in
+ * turn stall out many other dependent tasks, without us being
+ * able to determine whether they will ever require compensation.
+ * Even though work-stealing otherwise encounters little
+ * degradation in the presence of more threads than cores,
+ * aggressively adding new threads in such cases entails risk of
+ * unwanted positive feedback control loops in which more threads
+ * cause more dependent stalls (as well as delayed progress of
+ * unblocked threads to the point that we know they are available)
+ * leading to more situations requiring more threads, and so
+ * on. This aspect of control can be seen as an (analytically
+ * intractable) game with an opponent that may choose the worst
+ * (for us) active thread to stall at any time. We take several
+ * precautions to bound losses (and thus bound gains), mainly in
+ * methods tryCompensate and awaitJoin.
+ *
+ * Common Pool
+ * ===========
*
- * Shutdown and Termination. A call to shutdownNow atomically sets
- * the ctl stop bit and then (non-atomically) sets each workers
- * "terminate" status, cancels all unprocessed tasks, and wakes up
- * all waiting workers. Detecting whether termination should
- * commence after a non-abrupt shutdown() call requires more work
- * and bookkeeping. We need consensus about quiesence (i.e., that
- * there is no more work) which is reflected in active counts so
- * long as there are no current blockers, as well as possible
- * re-evaluations during independent changes in blocking or
- * quiescing workers.
+ * The static commonPool always exists after static
+ * initialization. Since it (or any other created pool) need
+ * never be used, we minimize initial construction overhead and
+ * footprint to the setup of about a dozen fields, with no nested
+ * allocation. Most bootstrapping occurs within method
+ * fullExternalPush during the first submission to the pool.
*
- * Style notes: There is a lot of representation-level coupling
- * among classes ForkJoinPool, ForkJoinWorkerThread, and
- * ForkJoinTask. Most fields of ForkJoinWorkerThread maintain
- * data structures managed by ForkJoinPool, so are directly
- * accessed. Conversely we allow access to "workers" array by
- * workers, and direct access to ForkJoinTask.status by both
- * ForkJoinPool and ForkJoinWorkerThread. There is little point
+ * When external threads submit to the common pool, they can
+ * perform some subtask processing (see externalHelpJoin and
+ * related methods). We do not need to record whether these
+ * submissions are to the common pool -- if not, externalHelpJoin
+ * returns quickly (at the most helping to signal some common pool
+ * workers). These submitters would otherwise be blocked waiting
+ * for completion, so the extra effort (with liberally sprinkled
+ * task status checks) in inapplicable cases amounts to an odd
+ * form of limited spin-wait before blocking in ForkJoinTask.join.
+ *
+ * Style notes
+ * ===========
+ *
+ * There is a lot of representation-level coupling among classes
+ * ForkJoinPool, ForkJoinWorkerThread, and ForkJoinTask. The
+ * fields of WorkQueue maintain data structures managed by
+ * ForkJoinPool, so are directly accessed. There is little point
* trying to reduce this, since any associated future changes in
* representations will need to be accompanied by algorithmic
- * changes anyway. All together, these low-level implementation
- * choices produce as much as a factor of 4 performance
- * improvement compared to naive implementations, and enable the
- * processing of billions of tasks per second, at the expense of
- * some ugliness.
+ * changes anyway. Several methods intrinsically sprawl because
+ * they must accumulate sets of consistent reads of volatiles held
+ * in local variables. Methods signalWork() and scan() are the
+ * main bottlenecks, so are especially heavily
+ * micro-optimized/mangled. There are lots of inline assignments
+ * (of form "while ((local = field) != 0)") which are usually the
+ * simplest way to ensure the required read orderings (which are
+ * sometimes critical). This leads to a "C"-like style of listing
+ * declarations of these locals at the heads of methods or blocks.
+ * There are several occurrences of the unusual "do {} while
+ * (!cas...)" which is the simplest way to force an update of a
+ * CAS'ed variable. There are also other coding oddities (including
+ * several unnecessary-looking hoisted null checks) that help
+ * some methods perform reasonably even when interpreted (not
+ * compiled).
*
- * Methods signalWork() and scan() are the main bottlenecks so are
- * especially heavily micro-optimized/mangled. There are lots of
- * inline assignments (of form "while ((local = field) != 0)")
- * which are usually the simplest way to ensure the required read
- * orderings (which are sometimes critical). This leads to a
- * "C"-like style of listing declarations of these locals at the
- * heads of methods or blocks. There are several occurrences of
- * the unusual "do {} while (!cas...)" which is the simplest way
- * to force an update of a CAS'ed variable. There are also other
- * coding oddities that help some methods perform reasonably even
- * when interpreted (not compiled).
- *
- * The order of declarations in this file is: (1) declarations of
- * statics (2) fields (along with constants used when unpacking
- * some of them), listed in an order that tends to reduce
- * contention among them a bit under most JVMs. (3) internal
- * control methods (4) callbacks and other support for
- * ForkJoinTask and ForkJoinWorkerThread classes, (5) exported
- * methods (plus a few little helpers). (6) static block
- * initializing all statics in a minimally dependent order.
+ * The order of declarations in this file is:
+ * (1) Static utility functions
+ * (2) Nested (static) classes
+ * (3) Static fields
+ * (4) Fields, along with constants used when unpacking some of them
+ * (5) Internal control methods
+ * (6) Callbacks and other support for ForkJoinTask methods
+ * (7) Exported methods
+ * (8) Static block initializing statics in minimally dependent order
*/
+ // Static utilities
+
+ /**
+ * If there is a security manager, makes sure caller has
+ * permission to modify threads.
+ */
+ private static void checkPermission() {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null)
+ security.checkPermission(modifyThreadPermission);
+ }
+
+ // Nested classes
+
/**
* Factory for creating new {@link ForkJoinWorkerThread}s.
* A {@code ForkJoinWorkerThreadFactory} must be defined and used
@@ -384,14 +556,526 @@
* Default ForkJoinWorkerThreadFactory implementation; creates a
* new ForkJoinWorkerThread.
*/
- static class DefaultForkJoinWorkerThreadFactory
+ static final class DefaultForkJoinWorkerThreadFactory
implements ForkJoinWorkerThreadFactory {
- public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
+ public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
return new ForkJoinWorkerThread(pool);
}
}
/**
+ * Per-thread records for threads that submit to pools. Currently
+ * holds only pseudo-random seed / index that is used to choose
+ * submission queues in method externalPush. In the future, this may
+ * also incorporate a means to implement different task rejection
+ * and resubmission policies.
+ *
+ * Seeds for submitters and workers/workQueues work in basically
+ * the same way but are initialized and updated using slightly
+ * different mechanics. Both are initialized using the same
+ * approach as in class ThreadLocal, where successive values are
+ * unlikely to collide with previous values. Seeds are then
+ * randomly modified upon collisions using xorshifts, which
+ * requires a non-zero seed.
+ */
+ static final class Submitter {
+ int seed;
+ Submitter(int s) { seed = s; }
+ }
+
+ /**
+ * Class for artificial tasks that are used to replace the target
+ * of local joins if they are removed from an interior queue slot
+ * in WorkQueue.tryRemoveAndExec. We don't need the proxy to
+ * actually do anything beyond having a unique identity.
+ */
+ static final class EmptyTask extends ForkJoinTask<Void> {
+ private static final long serialVersionUID = -7721805057305804111L;
+ EmptyTask() { status = ForkJoinTask.NORMAL; } // force done
+ public final Void getRawResult() { return null; }
+ public final void setRawResult(Void x) {}
+ public final boolean exec() { return true; }
+ }
+
+ /**
+ * Queues supporting work-stealing as well as external task
+ * submission. See above for main rationale and algorithms.
+ * Implementation relies heavily on "Unsafe" intrinsics
+ * and selective use of "volatile":
+ *
+ * Field "base" is the index (mod array.length) of the least valid
+ * queue slot, which is always the next position to steal (poll)
+ * from if nonempty. Reads and writes require volatile orderings
+ * but not CAS, because updates are only performed after slot
+ * CASes.
+ *
+ * Field "top" is the index (mod array.length) of the next queue
+ * slot to push to or pop from. It is written only by owner thread
+ * for push, or under lock for external/shared push, and accessed
+ * by other threads only after reading (volatile) base. Both top
+ * and base are allowed to wrap around on overflow, but (top -
+ * base) (or more commonly -(base - top) to force volatile read of
+ * base before top) still estimates size. The lock ("qlock") is
+ * forced to -1 on termination, causing all further lock attempts
+ * to fail. (Note: we don't need CAS for termination state because
+ * upon pool shutdown, all shared-queues will stop being used
+ * anyway.) Nearly all lock bodies are set up so that exceptions
+ * within lock bodies are "impossible" (modulo JVM errors that
+ * would cause failure anyway.)
+ *
+ * The array slots are read and written using the emulation of
+ * volatiles/atomics provided by Unsafe. Insertions must in
+ * general use putOrderedObject as a form of releasing store to
+ * ensure that all writes to the task object are ordered before
+ * its publication in the queue. All removals entail a CAS to
+ * null. The array is always a power of two. To ensure safety of
+ * Unsafe array operations, all accesses perform explicit null
+ * checks and implicit bounds checks via power-of-two masking.
+ *
+ * In addition to basic queuing support, this class contains
+ * fields described elsewhere to control execution. It turns out
+ * to work better memory-layout-wise to include them in this class
+ * rather than a separate class.
+ *
+ * Performance on most platforms is very sensitive to placement of
+ * instances of both WorkQueues and their arrays -- we absolutely
+ * do not want multiple WorkQueue instances or multiple queue
+ * arrays sharing cache lines. (It would be best for queue objects
+ * and their arrays to share, but there is nothing available to
+ * help arrange that). Unfortunately, because they are recorded
+ * in a common array, WorkQueue instances are often moved to be
+ * adjacent by garbage collectors. To reduce impact, we use field
+ * padding that works OK on common platforms; this effectively
+ * trades off slightly slower average field access for the sake of
+ * avoiding really bad worst-case access. (Until better JVM
+ * support is in place, this padding is dependent on transient
+ * properties of JVM field layout rules.) We also take care in
+ * allocating, sizing and resizing the array. Non-shared queue
+ * arrays are initialized by workers before use. Others are
+ * allocated on first use.
+ */
+ static final class WorkQueue {
+ /**
+ * Capacity of work-stealing queue array upon initialization.
+ * Must be a power of two; at least 4, but should be larger to
+ * reduce or eliminate cacheline sharing among queues.
+ * Currently, it is much larger, as a partial workaround for
+ * the fact that JVMs often place arrays in locations that
+ * share GC bookkeeping (especially cardmarks) such that
+ * per-write accesses encounter serious memory contention.
+ */
+ static final int INITIAL_QUEUE_CAPACITY = 1 << 13;
+
+ /**
+ * Maximum size for queue arrays. Must be a power of two less
+ * than or equal to 1 << (31 - width of array entry) to ensure
+ * lack of wraparound of index calculations, but defined to a
+ * value a bit less than this to help users trap runaway
+ * programs before saturating systems.
+ */
+ static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 64M
+
+ // Heuristic padding to ameliorate unfortunate memory placements
+ volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06;
+
+ int seed; // for random scanning; initialize nonzero
+ volatile int eventCount; // encoded inactivation count; < 0 if inactive
+ int nextWait; // encoded record of next event waiter
+ int hint; // steal or signal hint (index)
+ int poolIndex; // index of this queue in pool (or 0)
+ final int mode; // 0: lifo, > 0: fifo, < 0: shared
+ int nsteals; // number of steals
+ volatile int qlock; // 1: locked, -1: terminate; else 0
+ volatile int base; // index of next slot for poll
+ int top; // index of next slot for push
+ ForkJoinTask<?>[] array; // the elements (initially unallocated)
+ final ForkJoinPool pool; // the containing pool (may be null)
+ final ForkJoinWorkerThread owner; // owning thread or null if shared
+ volatile Thread parker; // == owner during call to park; else null
+ volatile ForkJoinTask<?> currentJoin; // task being joined in awaitJoin
+ ForkJoinTask<?> currentSteal; // current non-local task being executed
+
+ volatile Object pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17;
+ volatile Object pad18, pad19, pad1a, pad1b, pad1c, pad1d;
+
+ WorkQueue(ForkJoinPool pool, ForkJoinWorkerThread owner, int mode,
+ int seed) {
+ this.pool = pool;
+ this.owner = owner;
+ this.mode = mode;
+ this.seed = seed;
+ // Place indices in the center of array (that is not yet allocated)
+ base = top = INITIAL_QUEUE_CAPACITY >>> 1;
+ }
+
+ /**
+ * Returns the approximate number of tasks in the queue.
+ */
+ final int queueSize() {
+ int n = base - top; // non-owner callers must read base first
+ return (n >= 0) ? 0 : -n; // ignore transient negative
+ }
+
+ /**
+ * Provides a more accurate estimate of whether this queue has
+ * any tasks than does queueSize, by checking whether a
+ * near-empty queue has at least one unclaimed task.
+ */
+ final boolean isEmpty() {
+ ForkJoinTask<?>[] a; int m, s;
+ int n = base - (s = top);
+ return (n >= 0 ||
+ (n == -1 &&
+ ((a = array) == null ||
+ (m = a.length - 1) < 0 ||
+ U.getObject
+ (a, (long)((m & (s - 1)) << ASHIFT) + ABASE) == null)));
+ }
+
+ /**
+ * Pushes a task. Call only by owner in unshared queues. (The
+ * shared-queue version is embedded in method externalPush.)
+ *
+ * @param task the task. Caller must ensure non-null.
+ * @throw RejectedExecutionException if array cannot be resized
+ */
+ final void push(ForkJoinTask<?> task) {
+ ForkJoinTask<?>[] a; ForkJoinPool p;
+ int s = top, m, n;
+ if ((a = array) != null) { // ignore if queue removed
+ int j = (((m = a.length - 1) & s) << ASHIFT) + ABASE;
+ U.putOrderedObject(a, j, task);
+ if ((n = (top = s + 1) - base) <= 2) {
+ if ((p = pool) != null)
+ p.signalWork(this);
+ }
+ else if (n >= m)
+ growArray();
+ }
+ }
+
+ /**
+ * Initializes or doubles the capacity of array. Call either
+ * by owner or with lock held -- it is OK for base, but not
+ * top, to move while resizings are in progress.
+ */
+ final ForkJoinTask<?>[] growArray() {
+ ForkJoinTask<?>[] oldA = array;
+ int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;
+ if (size > MAXIMUM_QUEUE_CAPACITY)
+ throw new RejectedExecutionException("Queue capacity exceeded");
+ int oldMask, t, b;
+ ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];
+ if (oldA != null && (oldMask = oldA.length - 1) >= 0 &&
+ (t = top) - (b = base) > 0) {
+ int mask = size - 1;
+ do {
+ ForkJoinTask<?> x;
+ int oldj = ((b & oldMask) << ASHIFT) + ABASE;
+ int j = ((b & mask) << ASHIFT) + ABASE;
+ x = (ForkJoinTask<?>)U.getObjectVolatile(oldA, oldj);
+ if (x != null &&
+ U.compareAndSwapObject(oldA, oldj, x, null))
+ U.putObjectVolatile(a, j, x);
+ } while (++b != t);
+ }
+ return a;
+ }
+
+ /**
+ * Takes next task, if one exists, in LIFO order. Call only
+ * by owner in unshared queues.
+ */
+ final ForkJoinTask<?> pop() {
+ ForkJoinTask<?>[] a; ForkJoinTask<?> t; int m;
+ if ((a = array) != null && (m = a.length - 1) >= 0) {
+ for (int s; (s = top - 1) - base >= 0;) {
+ long j = ((m & s) << ASHIFT) + ABASE;
+ if ((t = (ForkJoinTask<?>)U.getObject(a, j)) == null)
+ break;
+ if (U.compareAndSwapObject(a, j, t, null)) {
+ top = s;
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Takes a task in FIFO order if b is base of queue and a task
+ * can be claimed without contention. Specialized versions
+ * appear in ForkJoinPool methods scan and tryHelpStealer.
+ */
+ final ForkJoinTask<?> pollAt(int b) {
+ ForkJoinTask<?> t; ForkJoinTask<?>[] a;
+ if ((a = array) != null) {
+ int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
+ if ((t = (ForkJoinTask<?>)U.getObjectVolatile(a, j)) != null &&
+ base == b &&
+ U.compareAndSwapObject(a, j, t, null)) {
+ base = b + 1;
+ return t;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Takes next task, if one exists, in FIFO order.
+ */
+ final ForkJoinTask<?> poll() {
+ ForkJoinTask<?>[] a; int b; ForkJoinTask<?> t;
+ while ((b = base) - top < 0 && (a = array) != null) {
+ int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
+ t = (ForkJoinTask<?>)U.getObjectVolatile(a, j);
+ if (t != null) {
+ if (base == b &&
+ U.compareAndSwapObject(a, j, t, null)) {
+ base = b + 1;
+ return t;
+ }
+ }
+ else if (base == b) {
+ if (b + 1 == top)
+ break;
+ Thread.yield(); // wait for lagging update (very rare)
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Takes next task, if one exists, in order specified by mode.
+ */
+ final ForkJoinTask<?> nextLocalTask() {
+ return mode == 0 ? pop() : poll();
+ }
+
+ /**
+ * Returns next task, if one exists, in order specified by mode.
+ */
+ final ForkJoinTask<?> peek() {
+ ForkJoinTask<?>[] a = array; int m;
+ if (a == null || (m = a.length - 1) < 0)
+ return null;
+ int i = mode == 0 ? top - 1 : base;
+ int j = ((i & m) << ASHIFT) + ABASE;
+ return (ForkJoinTask<?>)U.getObjectVolatile(a, j);
+ }
+
+ /**
+ * Pops the given task only if it is at the current top.
+ * (A shared version is available only via FJP.tryExternalUnpush)
+ */
+ final boolean tryUnpush(ForkJoinTask<?> t) {
+ ForkJoinTask<?>[] a; int s;
+ if ((a = array) != null && (s = top) != base &&
+ U.compareAndSwapObject
+ (a, (((a.length - 1) & --s) << ASHIFT) + ABASE, t, null)) {
+ top = s;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes and cancels all known tasks, ignoring any exceptions.
+ */
+ final void cancelAll() {
+ ForkJoinTask.cancelIgnoringExceptions(currentJoin);
+ ForkJoinTask.cancelIgnoringExceptions(currentSteal);
+ for (ForkJoinTask<?> t; (t = poll()) != null; )
+ ForkJoinTask.cancelIgnoringExceptions(t);
+ }
+
+ /**
+ * Computes next value for random probes. Scans don't require
+ * a very high quality generator, but also not a crummy one.
+ * Marsaglia xor-shift is cheap and works well enough. Note:
+ * This is manually inlined in its usages in ForkJoinPool to
+ * avoid writes inside busy scan loops.
+ */
+ final int nextSeed() {
+ int r = seed;
+ r ^= r << 13;
+ r ^= r >>> 17;
+ return seed = r ^= r << 5;
+ }
+
+ // Specialized execution methods
+
+ /**
+ * Pops and runs tasks until empty.
+ */
+ private void popAndExecAll() {
+ // A bit faster than repeated pop calls
+ ForkJoinTask<?>[] a; int m, s; long j; ForkJoinTask<?> t;
+ while ((a = array) != null && (m = a.length - 1) >= 0 &&
+ (s = top - 1) - base >= 0 &&
+ (t = ((ForkJoinTask<?>)
+ U.getObject(a, j = ((m & s) << ASHIFT) + ABASE)))
+ != null) {
+ if (U.compareAndSwapObject(a, j, t, null)) {
+ top = s;
+ t.doExec();
+ }
+ }
+ }
+
+ /**
+ * Polls and runs tasks until empty.
+ */
+ private void pollAndExecAll() {
+ for (ForkJoinTask<?> t; (t = poll()) != null;)
+ t.doExec();
+ }
+
+ /**
+ * If present, removes from queue and executes the given task,
+ * or any other cancelled task. Returns (true) on any CAS
+ * or consistency check failure so caller can retry.
+ *
+ * @return false if no progress can be made, else true;
+ */
+ final boolean tryRemoveAndExec(ForkJoinTask<?> task) {
+ boolean stat = true, removed = false, empty = true;
+ ForkJoinTask<?>[] a; int m, s, b, n;
+ if ((a = array) != null && (m = a.length - 1) >= 0 &&
+ (n = (s = top) - (b = base)) > 0) {
+ for (ForkJoinTask<?> t;;) { // traverse from s to b
+ int j = ((--s & m) << ASHIFT) + ABASE;
+ t = (ForkJoinTask<?>)U.getObjectVolatile(a, j);
+ if (t == null) // inconsistent length
+ break;
+ else if (t == task) {
+ if (s + 1 == top) { // pop
+ if (!U.compareAndSwapObject(a, j, task, null))
+ break;
+ top = s;
+ removed = true;
+ }
+ else if (base == b) // replace with proxy
+ removed = U.compareAndSwapObject(a, j, task,
+ new EmptyTask());
+ break;
+ }
+ else if (t.status >= 0)
+ empty = false;
+ else if (s + 1 == top) { // pop and throw away
+ if (U.compareAndSwapObject(a, j, t, null))
+ top = s;
+ break;
+ }
+ if (--n == 0) {
+ if (!empty && base == b)
+ stat = false;
+ break;
+ }
+ }
+ }
+ if (removed)
+ task.doExec();
+ return stat;
+ }
+
+ /**
+ * Polls for and executes the given task or any other task in
+ * its CountedCompleter computation
+ */
+ final boolean pollAndExecCC(ForkJoinTask<?> root) {
+ ForkJoinTask<?>[] a; int b; Object o;
+ outer: while ((b = base) - top < 0 && (a = array) != null) {
+ long j = (((a.length - 1) & b) << ASHIFT) + ABASE;
+ if ((o = U.getObject(a, j)) == null ||
+ !(o instanceof CountedCompleter))
+ break;
+ for (CountedCompleter<?> t = (CountedCompleter<?>)o, r = t;;) {
+ if (r == root) {
+ if (base == b &&
+ U.compareAndSwapObject(a, j, t, null)) {
+ base = b + 1;
+ t.doExec();
+ return true;
+ }
+ else
+ break; // restart
+ }
+ if ((r = r.completer) == null)
+ break outer; // not part of root computation
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Executes a top-level task and any local tasks remaining
+ * after execution.
+ */
+ final void runTask(ForkJoinTask<?> t) {
+ if (t != null) {
+ (currentSteal = t).doExec();
+ currentSteal = null;
+ ++nsteals;
+ if (base - top < 0) { // process remaining local tasks
+ if (mode == 0)
+ popAndExecAll();
+ else
+ pollAndExecAll();
+ }
+ }
+ }
+
+ /**
+ * Executes a non-top-level (stolen) task.
+ */
+ final void runSubtask(ForkJoinTask<?> t) {
+ if (t != null) {
+ ForkJoinTask<?> ps = currentSteal;
+ (currentSteal = t).doExec();
+ currentSteal = ps;
+ }
+ }
+
+ /**
+ * Returns true if owned and not known to be blocked.
+ */
+ final boolean isApparentlyUnblocked() {
+ Thread wt; Thread.State s;
+ return (eventCount >= 0 &&
+ (wt = owner) != null &&
+ (s = wt.getState()) != Thread.State.BLOCKED &&
+ s != Thread.State.WAITING &&
+ s != Thread.State.TIMED_WAITING);
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U;
+ private static final long QLOCK;
+ private static final int ABASE;
+ private static final int ASHIFT;
+ static {
+ int s;
+ try {
+ U = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = WorkQueue.class;
+ Class<?> ak = ForkJoinTask[].class;
+ QLOCK = U.objectFieldOffset
+ (k.getDeclaredField("qlock"));
+ ABASE = U.arrayBaseOffset(ak);
+ s = U.arrayIndexScale(ak);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ if ((s & (s-1)) != 0)
+ throw new Error("data type scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
+ }
+ }
+
+ // static fields (initialized in static initializer below)
+
+ /**
* Creates a new ForkJoinWorkerThread. This factory is used unless
* overridden in ForkJoinPool constructors.
*/
@@ -399,107 +1083,93 @@
defaultForkJoinWorkerThreadFactory;
/**
+ * Per-thread submission bookkeeping. Shared across all pools
+ * to reduce ThreadLocal pollution and because random motion
+ * to avoid contention in one pool is likely to hold for others.
+ * Lazily initialized on first submission (but null-checked
+ * in other contexts to avoid unnecessary initialization).
+ */
+ static final ThreadLocal<Submitter> submitters;
+
+ /**
* Permission required for callers of methods that may start or
* kill threads.
*/
private static final RuntimePermission modifyThreadPermission;
/**
- * If there is a security manager, makes sure caller has
- * permission to modify threads.
+ * Common (static) pool. Non-null for public use unless a static
+ * construction exception, but internal usages null-check on use
+ * to paranoically avoid potential initialization circularities
+ * as well as to simplify generated code.
*/
- private static void checkPermission() {
- SecurityManager security = System.getSecurityManager();
- if (security != null)
- security.checkPermission(modifyThreadPermission);
- }
+ static final ForkJoinPool commonPool;
/**
- * Generator for assigning sequence numbers as pool names.
+ * Common pool parallelism. Must equal commonPool.parallelism.
*/
- private static final AtomicInteger poolNumberGenerator;
+ static final int commonPoolParallelism;
/**
- * Generator for initial random seeds for worker victim
- * selection. This is used only to create initial seeds. Random
- * steals use a cheaper xorshift generator per steal attempt. We
- * don't expect much contention on seedGenerator, so just use a
- * plain Random.
+ * Sequence number for creating workerNamePrefix.
*/
- static final Random workerSeedGenerator;
+ private static int poolNumberSequence;
/**
- * Array holding all worker threads in the pool. Initialized upon
- * construction. Array size must be a power of two. Updates and
- * replacements are protected by scanGuard, but the array is
- * always kept in a consistent enough state to be randomly
- * accessed without locking by workers performing work-stealing,
- * as well as other traversal-based methods in this class, so long
- * as reads memory-acquire by first reading ctl. All readers must
- * tolerate that some array slots may be null.
+ * Return the next sequence number. We don't expect this to
+ * ever contend so use simple builtin sync.
*/
- ForkJoinWorkerThread[] workers;
+ private static final synchronized int nextPoolId() {
+ return ++poolNumberSequence;
+ }
- /**
- * Initial size for submission queue array. Must be a power of
- * two. In many applications, these always stay small so we use a
- * small initial cap.
- */
- private static final int INITIAL_QUEUE_CAPACITY = 8;
+ // static constants
/**
- * Maximum size for submission queue array. Must be a power of two
- * less than or equal to 1 << (31 - width of array entry) to
- * ensure lack of index wraparound, but is capped at a lower
- * value to help users trap runaway computations.
+ * Initial timeout value (in nanoseconds) for the thread
+ * triggering quiescence to park waiting for new work. On timeout,
+ * the thread will instead try to shrink the number of
+ * workers. The value should be large enough to avoid overly
+ * aggressive shrinkage during most transient stalls (long GCs
+ * etc).
*/
- private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 24; // 16M
+ private static final long IDLE_TIMEOUT = 2000L * 1000L * 1000L; // 2sec
/**
- * Array serving as submission queue. Initialized upon construction.
+ * Timeout value when there are more threads than parallelism level
*/
- private ForkJoinTask<?>[] submissionQueue;
+ private static final long FAST_IDLE_TIMEOUT = 200L * 1000L * 1000L;
/**
- * Lock protecting submissions array for addSubmission
+ * Tolerance for idle timeouts, to cope with timer undershoots
*/
- private final ReentrantLock submissionLock;
-
- /**
- * Condition for awaitTermination, using submissionLock for
- * convenience.
- */
- private final Condition termination;
+ private static final long TIMEOUT_SLOP = 2000000L;
/**
- * Creation factory for worker threads.
+ * The maximum stolen->joining link depth allowed in method
+ * tryHelpStealer. Must be a power of two. Depths for legitimate
+ * chains are unbounded, but we use a fixed constant to avoid
+ * (otherwise unchecked) cycles and to bound staleness of
+ * traversal parameters at the expense of sometimes blocking when
+ * we could be helping.
*/
- private final ForkJoinWorkerThreadFactory factory;
-
- /**
- * The uncaught exception handler used when any worker abruptly
- * terminates.
- */
- final Thread.UncaughtExceptionHandler ueh;
+ private static final int MAX_HELP = 64;
/**
- * Prefix for assigning names to worker threads
+ * Increment for seed generators. See class ThreadLocal for
+ * explanation.
*/
- private final String workerNamePrefix;
+ private static final int SEED_INCREMENT = 0x61c88647;
/**
- * Sum of per-thread steal counts, updated only when threads are
- * idle or terminating.
- */
- private volatile long stealCount;
-
- /**
- * Main pool control -- a long packed with:
+ * Bits and masks for control variables
+ *
+ * Field ctl is a long packed with:
* AC: Number of active running workers minus target parallelism (16 bits)
- * TC: Number of total workers minus target parallelism (16bits)
+ * TC: Number of total workers minus target parallelism (16 bits)
* ST: true if pool is terminating (1 bit)
* EC: the wait count of top waiting thread (15 bits)
- * ID: ~poolIndex of top of Treiber stack of waiting threads (16 bits)
+ * ID: poolIndex of top of Treiber stack of waiters (16 bits)
*
* When convenient, we can extract the upper 32 bits of counts and
* the lower 32 bits of queue state, u = (int)(ctl >>> 32) and e =
@@ -508,13 +1178,26 @@
* parallelism and the positionings of fields makes it possible to
* perform the most common checks via sign tests of fields: When
* ac is negative, there are not enough active workers, when tc is
- * negative, there are not enough total workers, when id is
- * negative, there is at least one waiting worker, and when e is
+ * negative, there are not enough total workers, and when e is
* negative, the pool is terminating. To deal with these possibly
* negative fields, we use casts in and out of "short" and/or
* signed shifts to maintain signedness.
+ *
+ * When a thread is queued (inactivated), its eventCount field is
+ * set negative, which is the only way to tell if a worker is
+ * prevented from executing tasks, even though it must continue to
+ * scan for them to avoid queuing races. Note however that
+ * eventCount updates lag releases so usage requires care.
+ *
+ * Field plock is an int packed with:
+ * SHUTDOWN: true if shutdown is enabled (1 bit)
+ * SEQ: a sequence lock, with PL_LOCK bit set if locked (30 bits)
+ * SIGNAL: set when threads may be waiting on the lock (1 bit)
+ *
+ * The sequence number enables simple consistency checks:
+ * Staleness of read-only operations on the workQueues array can
+ * be checked by comparing plock before vs after the reads.
*/
- volatile long ctl;
// bit positions/shifts for fields
private static final int AC_SHIFT = 48;
@@ -523,8 +1206,10 @@
private static final int EC_SHIFT = 16;
// bounds
- private static final int MAX_ID = 0x7fff; // max poolIndex
- private static final int SMASK = 0xffff; // mask short bits
+ private static final int SMASK = 0xffff; // short bits
+ private static final int MAX_CAP = 0x7fff; // max #workers - 1
+ private static final int EVENMASK = 0xfffe; // even short bits
+ private static final int SQMASK = 0x007e; // max 64 (even) slots
private static final int SHORT_SIGN = 1 << 15;
private static final int INT_SIGN = 1 << 31;
@@ -546,649 +1231,648 @@
private static final int UTC_UNIT = 1 << UTC_SHIFT;
// masks and units for dealing with e = (int)ctl
- private static final int E_MASK = 0x7fffffff; // no STOP_BIT
- private static final int EC_UNIT = 1 << EC_SHIFT;
-
- /**
- * The target parallelism level.
- */
- final int parallelism;
+ private static final int E_MASK = 0x7fffffff; // no STOP_BIT
+ private static final int E_SEQ = 1 << EC_SHIFT;
- /**
- * Index (mod submission queue length) of next element to take
- * from submission queue. Usage is identical to that for
- * per-worker queues -- see ForkJoinWorkerThread internal
- * documentation.
- */
- volatile int queueBase;
-
- /**
- * Index (mod submission queue length) of next element to add
- * in submission queue. Usage is identical to that for
- * per-worker queues -- see ForkJoinWorkerThread internal
- * documentation.
- */
- int queueTop;
+ // plock bits
+ private static final int SHUTDOWN = 1 << 31;
+ private static final int PL_LOCK = 2;
+ private static final int PL_SIGNAL = 1;
+ private static final int PL_SPINS = 1 << 8;
- /**
- * True when shutdown() has been called.
- */
- volatile boolean shutdown;
-
- /**
- * True if use local fifo, not default lifo, for local polling
- * Read by, and replicated by ForkJoinWorkerThreads
- */
- final boolean locallyFifo;
+ // access mode for WorkQueue
+ static final int LIFO_QUEUE = 0;
+ static final int FIFO_QUEUE = 1;
+ static final int SHARED_QUEUE = -1;
- /**
- * The number of threads in ForkJoinWorkerThreads.helpQuiescePool.
- * When non-zero, suppresses automatic shutdown when active
- * counts become zero.
- */
- volatile int quiescerCount;
+ // bounds for #steps in scan loop -- must be power 2 minus 1
+ private static final int MIN_SCAN = 0x1ff; // cover estimation slop
+ private static final int MAX_SCAN = 0x1ffff; // 4 * max workers
+
+ // Instance fields
- /**
- * The number of threads blocked in join.
- */
- volatile int blockedCount;
-
- /**
- * Counter for worker Thread names (unrelated to their poolIndex)
+ /*
+ * Field layout of this class tends to matter more than one would
+ * like. Runtime layout order is only loosely related to
+ * declaration order and may differ across JVMs, but the following
+ * empirically works OK on current JVMs.
*/
- private volatile int nextWorkerNumber;
- /**
- * The index for the next created worker. Accessed under scanGuard.
- */
- private int nextWorkerIndex;
+ // Heuristic padding to ameliorate unfortunate memory placements
+ volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06;
- /**
- * SeqLock and index masking for updates to workers array. Locked
- * when SG_UNIT is set. Unlocking clears bit by adding
- * SG_UNIT. Staleness of read-only operations can be checked by
- * comparing scanGuard to value before the reads. The low 16 bits
- * (i.e, anding with SMASK) hold (the smallest power of two
- * covering all worker indices, minus one, and is used to avoid
- * dealing with large numbers of null slots when the workers array
- * is overallocated.
- */
- volatile int scanGuard;
+ volatile long stealCount; // collects worker counts
+ volatile long ctl; // main pool control
+ volatile int plock; // shutdown status and seqLock
+ volatile int indexSeed; // worker/submitter index seed
+ final int config; // mode and parallelism level
+ WorkQueue[] workQueues; // main registry
+ final ForkJoinWorkerThreadFactory factory;
+ final Thread.UncaughtExceptionHandler ueh; // per-worker UEH
+ final String workerNamePrefix; // to create worker name string
- private static final int SG_UNIT = 1 << 16;
+ volatile Object pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17;
+ volatile Object pad18, pad19, pad1a, pad1b;
- /**
- * The wakeup interval (in nanoseconds) for a worker waiting for a
- * task when the pool is quiescent to instead try to shrink the
- * number of workers. The exact value does not matter too
- * much. It must be short enough to release resources during
- * sustained periods of idleness, but not so short that threads
- * are continually re-created.
+ /*
+ * Acquires the plock lock to protect worker array and related
+ * updates. This method is called only if an initial CAS on plock
+ * fails. This acts as a spinLock for normal cases, but falls back
+ * to builtin monitor to block when (rarely) needed. This would be
+ * a terrible idea for a highly contended lock, but works fine as
+ * a more conservative alternative to a pure spinlock.
*/
- private static final long SHRINK_RATE =
- 4L * 1000L * 1000L * 1000L; // 4 seconds
-
- /**
- * Top-level loop for worker threads: On each step: if the
- * previous step swept through all queues and found no tasks, or
- * there are excess threads, then possibly blocks. Otherwise,
- * scans for and, if found, executes a task. Returns when pool
- * and/or worker terminate.
- *
- * @param w the worker
- */
- final void work(ForkJoinWorkerThread w) {
- boolean swept = false; // true on empty scans
- long c;
- while (!w.terminate && (int)(c = ctl) >= 0) {
- int a; // active count
- if (!swept && (a = (int)(c >> AC_SHIFT)) <= 0)
- swept = scan(w, a);
- else if (tryAwaitWork(w, c))
- swept = false;
+ private int acquirePlock() {
+ int spins = PL_SPINS, r = 0, ps, nps;
+ for (;;) {
+ if (((ps = plock) & PL_LOCK) == 0 &&
+ U.compareAndSwapInt(this, PLOCK, ps, nps = ps + PL_LOCK))
+ return nps;
+ else if (r == 0) { // randomize spins if possible
+ Thread t = Thread.currentThread(); WorkQueue w; Submitter z;
+ if ((t instanceof ForkJoinWorkerThread) &&
+ (w = ((ForkJoinWorkerThread)t).workQueue) != null)
+ r = w.seed;
+ else if ((z = submitters.get()) != null)
+ r = z.seed;
+ else
+ r = 1;
+ }
+ else if (spins >= 0) {
+ r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift
+ if (r >= 0)
+ --spins;
+ }
+ else if (U.compareAndSwapInt(this, PLOCK, ps, ps | PL_SIGNAL)) {
+ synchronized (this) {
+ if ((plock & PL_SIGNAL) != 0) {
+ try {
+ wait();
+ } catch (InterruptedException ie) {
+ try {
+ Thread.currentThread().interrupt();
+ } catch (SecurityException ignore) {
+ }
+ }
+ }
+ else
+ notifyAll();
+ }
+ }
}
}
- // Signalling
+ /**
+ * Unlocks and signals any thread waiting for plock. Called only
+ * when CAS of seq value for unlock fails.
+ */
+ private void releasePlock(int ps) {
+ plock = ps;
+ synchronized (this) { notifyAll(); }
+ }
/**
- * Wakes up or creates a worker.
+ * Performs secondary initialization, called when plock is zero.
+ * Creates workQueue array and sets plock to a valid value. The
+ * lock body must be exception-free (so no try/finally) so we
+ * optimistically allocate new array outside the lock and throw
+ * away if (very rarely) not needed. (A similar tactic is used in
+ * fullExternalPush.) Because the plock seq value can eventually
+ * wrap around zero, this method harmlessly fails to reinitialize
+ * if workQueues exists, while still advancing plock.
+ *
+ * Additionally tries to create the first worker.
*/
- final void signalWork() {
- /*
- * The while condition is true if: (there is are too few total
- * workers OR there is at least one waiter) AND (there are too
- * few active workers OR the pool is terminating). The value
- * of e distinguishes the remaining cases: zero (no waiters)
- * for create, negative if terminating (in which case do
- * nothing), else release a waiter. The secondary checks for
- * release (non-null array etc) can fail if the pool begins
- * terminating after the test, and don't impose any added cost
- * because JVMs must perform null and bounds checks anyway.
- */
- long c; int e, u;
- while ((((e = (int)(c = ctl)) | (u = (int)(c >>> 32))) &
- (INT_SIGN|SHORT_SIGN)) == (INT_SIGN|SHORT_SIGN) && e >= 0) {
- if (e > 0) { // release a waiting worker
- int i; ForkJoinWorkerThread w; ForkJoinWorkerThread[] ws;
- if ((ws = workers) == null ||
- (i = ~e & SMASK) >= ws.length ||
- (w = ws[i]) == null)
- break;
- long nc = (((long)(w.nextWait & E_MASK)) |
- ((long)(u + UAC_UNIT) << 32));
- if (w.eventCount == e &&
- UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
- w.eventCount = (e + EC_UNIT) & E_MASK;
- if (w.parked)
- UNSAFE.unpark(w);
- break;
+ private void initWorkers() {
+ WorkQueue[] ws, nws; int ps;
+ int p = config & SMASK; // find power of two table size
+ int n = (p > 1) ? p - 1 : 1; // ensure at least 2 slots
+ n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16;
+ n = (n + 1) << 1;
+ if ((ws = workQueues) == null || ws.length == 0)
+ nws = new WorkQueue[n];
+ else
+ nws = null;
+ if (((ps = plock) & PL_LOCK) != 0 ||
+ !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+ ps = acquirePlock();
+ if (((ws = workQueues) == null || ws.length == 0) && nws != null)
+ workQueues = nws;
+ int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+ if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+ releasePlock(nps);
+ tryAddWorker();
+ }
+
+ /**
+ * Tries to create and start one worker if fewer than target
+ * parallelism level exist. Adjusts counts etc on failure.
+ */
+ private void tryAddWorker() {
+ long c; int u;
+ while ((u = (int)((c = ctl) >>> 32)) < 0 &&
+ (u & SHORT_SIGN) != 0 && (int)c == 0) {
+ long nc = (long)(((u + UTC_UNIT) & UTC_MASK) |
+ ((u + UAC_UNIT) & UAC_MASK)) << 32;
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ ForkJoinWorkerThreadFactory fac;
+ Throwable ex = null;
+ ForkJoinWorkerThread wt = null;
+ try {
+ if ((fac = factory) != null &&
+ (wt = fac.newThread(this)) != null) {
+ wt.start();
+ break;
+ }
+ } catch (Throwable e) {
+ ex = e;
}
- }
- else if (UNSAFE.compareAndSwapLong
- (this, ctlOffset, c,
- (long)(((u + UTC_UNIT) & UTC_MASK) |
- ((u + UAC_UNIT) & UAC_MASK)) << 32)) {
- addWorker();
+ deregisterWorker(wt, ex);
break;
}
}
}
+ // Registering and deregistering workers
+
/**
- * Variant of signalWork to help release waiters on rescans.
- * Tries once to release a waiter if active count < 0.
+ * Callback from ForkJoinWorkerThread to establish and record its
+ * WorkQueue. To avoid scanning bias due to packing entries in
+ * front of the workQueues array, we treat the array as a simple
+ * power-of-two hash table using per-thread seed as hash,
+ * expanding as needed.
+ *
+ * @param wt the worker thread
+ * @return the worker's queue
+ */
+ final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
+ Thread.UncaughtExceptionHandler handler; WorkQueue[] ws; int s, ps;
+ wt.setDaemon(true);
+ if ((handler = ueh) != null)
+ wt.setUncaughtExceptionHandler(handler);
+ do {} while (!U.compareAndSwapInt(this, INDEXSEED, s = indexSeed,
+ s += SEED_INCREMENT) ||
+ s == 0); // skip 0
+ WorkQueue w = new WorkQueue(this, wt, config >>> 16, s);
+ if (((ps = plock) & PL_LOCK) != 0 ||
+ !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+ ps = acquirePlock();
+ int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+ try {
+ if ((ws = workQueues) != null) { // skip if shutting down
+ int n = ws.length, m = n - 1;
+ int r = (s << 1) | 1; // use odd-numbered indices
+ if (ws[r &= m] != null) { // collision
+ int probes = 0; // step by approx half size
+ int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
+ while (ws[r = (r + step) & m] != null) {
+ if (++probes >= n) {
+ workQueues = ws = Arrays.copyOf(ws, n <<= 1);
+ m = n - 1;
+ probes = 0;
+ }
+ }
+ }
+ w.eventCount = w.poolIndex = r; // volatile write orders
+ ws[r] = w;
+ }
+ } finally {
+ if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+ releasePlock(nps);
+ }
+ wt.setName(workerNamePrefix.concat(Integer.toString(w.poolIndex)));
+ return w;
+ }
+
+ /**
+ * Final callback from terminating worker, as well as upon failure
+ * to construct or start a worker. Removes record of worker from
+ * array, and adjusts counts. If pool is shutting down, tries to
+ * complete termination.
*
- * @return false if failed due to contention, else true
+ * @param wt the worker thread or null if construction failed
+ * @param ex the exception causing failure, or null if none
+ */
+ final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) {
+ WorkQueue w = null;
+ if (wt != null && (w = wt.workQueue) != null) {
+ int ps;
+ w.qlock = -1; // ensure set
+ long ns = w.nsteals, sc; // collect steal count
+ do {} while (!U.compareAndSwapLong(this, STEALCOUNT,
+ sc = stealCount, sc + ns));
+ if (((ps = plock) & PL_LOCK) != 0 ||
+ !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+ ps = acquirePlock();
+ int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+ try {
+ int idx = w.poolIndex;
+ WorkQueue[] ws = workQueues;
+ if (ws != null && idx >= 0 && idx < ws.length && ws[idx] == w)
+ ws[idx] = null;
+ } finally {
+ if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+ releasePlock(nps);
+ }
+ }
+
+ long c; // adjust ctl counts
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, (((c - AC_UNIT) & AC_MASK) |
+ ((c - TC_UNIT) & TC_MASK) |
+ (c & ~(AC_MASK|TC_MASK)))));
+
+ if (!tryTerminate(false, false) && w != null && w.array != null) {
+ w.cancelAll(); // cancel remaining tasks
+ WorkQueue[] ws; WorkQueue v; Thread p; int u, i, e;
+ while ((u = (int)((c = ctl) >>> 32)) < 0 && (e = (int)c) >= 0) {
+ if (e > 0) { // activate or create replacement
+ if ((ws = workQueues) == null ||
+ (i = e & SMASK) >= ws.length ||
+ (v = ws[i]) != null)
+ break;
+ long nc = (((long)(v.nextWait & E_MASK)) |
+ ((long)(u + UAC_UNIT) << 32));
+ if (v.eventCount != (e | INT_SIGN))
+ break;
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ v.eventCount = (e + E_SEQ) & E_MASK;
+ if ((p = v.parker) != null)
+ U.unpark(p);
+ break;
+ }
+ }
+ else {
+ if ((short)u < 0)
+ tryAddWorker();
+ break;
+ }
+ }
+ }
+ if (ex == null) // help clean refs on way out
+ ForkJoinTask.helpExpungeStaleExceptions();
+ else // rethrow
+ ForkJoinTask.rethrow(ex);
+ }
+
+ // Submissions
+
+ /**
+ * Unless shutting down, adds the given task to a submission queue
+ * at submitter's current queue index (modulo submission
+ * range). Only the most common path is directly handled in this
+ * method. All others are relayed to fullExternalPush.
+ *
+ * @param task the task. Caller must ensure non-null.
*/
- private boolean tryReleaseWaiter() {
- long c; int e, i; ForkJoinWorkerThread w; ForkJoinWorkerThread[] ws;
- if ((e = (int)(c = ctl)) > 0 &&
- (int)(c >> AC_SHIFT) < 0 &&
- (ws = workers) != null &&
- (i = ~e & SMASK) < ws.length &&
- (w = ws[i]) != null) {
- long nc = ((long)(w.nextWait & E_MASK) |
- ((c + AC_UNIT) & (AC_MASK|TC_MASK)));
- if (w.eventCount != e ||
- !UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc))
- return false;
- w.eventCount = (e + EC_UNIT) & E_MASK;
- if (w.parked)
- UNSAFE.unpark(w);
+ final void externalPush(ForkJoinTask<?> task) {
+ WorkQueue[] ws; WorkQueue q; Submitter z; int m; ForkJoinTask<?>[] a;
+ if ((z = submitters.get()) != null && plock > 0 &&
+ (ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&
+ (q = ws[m & z.seed & SQMASK]) != null &&
+ U.compareAndSwapInt(q, QLOCK, 0, 1)) { // lock
+ int b = q.base, s = q.top, n, an;
+ if ((a = q.array) != null && (an = a.length) > (n = s + 1 - b)) {
+ int j = (((an - 1) & s) << ASHIFT) + ABASE;
+ U.putOrderedObject(a, j, task);
+ q.top = s + 1; // push on to deque
+ q.qlock = 0;
+ if (n <= 2)
+ signalWork(q);
+ return;
+ }
+ q.qlock = 0;
}
- return true;
+ fullExternalPush(task);
+ }
+
+ /**
+ * Full version of externalPush. This method is called, among
+ * other times, upon the first submission of the first task to the
+ * pool, so must perform secondary initialization (via
+ * initWorkers). It also detects first submission by an external
+ * thread by looking up its ThreadLocal, and creates a new shared
+ * queue if the one at index if empty or contended. The plock lock
+ * body must be exception-free (so no try/finally) so we
+ * optimistically allocate new queues outside the lock and throw
+ * them away if (very rarely) not needed.
+ */
+ private void fullExternalPush(ForkJoinTask<?> task) {
+ int r = 0; // random index seed
+ for (Submitter z = submitters.get();;) {
+ WorkQueue[] ws; WorkQueue q; int ps, m, k;
+ if (z == null) {
+ if (U.compareAndSwapInt(this, INDEXSEED, r = indexSeed,
+ r += SEED_INCREMENT) && r != 0)
+ submitters.set(z = new Submitter(r));
+ }
+ else if (r == 0) { // move to a different index
+ r = z.seed;
+ r ^= r << 13; // same xorshift as WorkQueues
+ r ^= r >>> 17;
+ z.seed = r ^ (r << 5);
+ }
+ else if ((ps = plock) < 0)
+ throw new RejectedExecutionException();
+ else if (ps == 0 || (ws = workQueues) == null ||
+ (m = ws.length - 1) < 0)
+ initWorkers();
+ else if ((q = ws[k = r & m & SQMASK]) != null) {
+ if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {
+ ForkJoinTask<?>[] a = q.array;
+ int s = q.top;
+ boolean submitted = false;
+ try { // locked version of push
+ if ((a != null && a.length > s + 1 - q.base) ||
+ (a = q.growArray()) != null) { // must presize
+ int j = (((a.length - 1) & s) << ASHIFT) + ABASE;
+ U.putOrderedObject(a, j, task);
+ q.top = s + 1;
+ submitted = true;
+ }
+ } finally {
+ q.qlock = 0; // unlock
+ }
+ if (submitted) {
+ signalWork(q);
+ return;
+ }
+ }
+ r = 0; // move on failure
+ }
+ else if (((ps = plock) & PL_LOCK) == 0) { // create new queue
+ q = new WorkQueue(this, null, SHARED_QUEUE, r);
+ if (((ps = plock) & PL_LOCK) != 0 ||
+ !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+ ps = acquirePlock();
+ if ((ws = workQueues) != null && k < ws.length && ws[k] == null)
+ ws[k] = q;
+ int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+ if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+ releasePlock(nps);
+ }
+ else
+ r = 0; // try elsewhere while lock held
+ }
+ }
+
+ // Maintaining ctl counts
+
+ /**
+ * Increments active count; mainly called upon return from blocking.
+ */
+ final void incrementActiveCount() {
+ long c;
+ do {} while (!U.compareAndSwapLong(this, CTL, c = ctl, c + AC_UNIT));
+ }
+
+ /**
+ * Tries to create or activate a worker if too few are active.
+ *
+ * @param q the (non-null) queue holding tasks to be signalled
+ */
+ final void signalWork(WorkQueue q) {
+ int hint = q.poolIndex;
+ long c; int e, u, i, n; WorkQueue[] ws; WorkQueue w; Thread p;
+ while ((u = (int)((c = ctl) >>> 32)) < 0) {
+ if ((e = (int)c) > 0) {
+ if ((ws = workQueues) != null && ws.length > (i = e & SMASK) &&
+ (w = ws[i]) != null && w.eventCount == (e | INT_SIGN)) {
+ long nc = (((long)(w.nextWait & E_MASK)) |
+ ((long)(u + UAC_UNIT) << 32));
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ w.hint = hint;
+ w.eventCount = (e + E_SEQ) & E_MASK;
+ if ((p = w.parker) != null)
+ U.unpark(p);
+ break;
+ }
+ if (q.top - q.base <= 0)
+ break;
+ }
+ else
+ break;
+ }
+ else {
+ if ((short)u < 0)
+ tryAddWorker();
+ break;
+ }
+ }
}
// Scanning for tasks
/**
- * Scans for and, if found, executes one task. Scans start at a
- * random index of workers array, and randomly select the first
- * (2*#workers)-1 probes, and then, if all empty, resort to 2
- * circular sweeps, which is necessary to check quiescence. and
- * taking a submission only if no stealable tasks were found. The
- * steal code inside the loop is a specialized form of
- * ForkJoinWorkerThread.deqTask, followed bookkeeping to support
- * helpJoinTask and signal propagation. The code for submission
- * queues is almost identical. On each steal, the worker completes
- * not only the task, but also all local tasks that this task may
- * have generated. On detecting staleness or contention when
- * trying to take a task, this method returns without finishing
- * sweep, which allows global state rechecks before retry.
- *
- * @param w the worker
- * @param a the number of active workers
- * @return true if swept all queues without finding a task
+ * Top-level runloop for workers, called by ForkJoinWorkerThread.run.
*/
- private boolean scan(ForkJoinWorkerThread w, int a) {
- int g = scanGuard; // mask 0 avoids useless scans if only one active
- int m = (parallelism == 1 - a && blockedCount == 0) ? 0 : g & SMASK;
- ForkJoinWorkerThread[] ws = workers;
- if (ws == null || ws.length <= m) // staleness check
- return false;
- for (int r = w.seed, k = r, j = -(m + m); j <= m + m; ++j) {
- ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
- ForkJoinWorkerThread v = ws[k & m];
- if (v != null && (b = v.queueBase) != v.queueTop &&
- (q = v.queue) != null && (i = (q.length - 1) & b) >= 0) {
- long u = (i << ASHIFT) + ABASE;
- if ((t = q[i]) != null && v.queueBase == b &&
- UNSAFE.compareAndSwapObject(q, u, t, null)) {
- int d = (v.queueBase = b + 1) - v.queueTop;
- v.stealHint = w.poolIndex;
- if (d != 0)
- signalWork(); // propagate if nonempty
- w.execTask(t);
- }
- r ^= r << 13; r ^= r >>> 17; w.seed = r ^ (r << 5);
- return false; // store next seed
- }
- else if (j < 0) { // xorshift
- r ^= r << 13; r ^= r >>> 17; k = r ^= r << 5;
- }
- else
- ++k;
- }
- if (scanGuard != g) // staleness check
- return false;
- else { // try to take submission
- ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
- if ((b = queueBase) != queueTop &&
- (q = submissionQueue) != null &&
- (i = (q.length - 1) & b) >= 0) {
- long u = (i << ASHIFT) + ABASE;
- if ((t = q[i]) != null && queueBase == b &&
- UNSAFE.compareAndSwapObject(q, u, t, null)) {
- queueBase = b + 1;
- w.execTask(t);
- }
- return false;
- }
- return true; // all queues empty
- }
+ final void runWorker(WorkQueue w) {
+ w.growArray(); // allocate queue
+ do { w.runTask(scan(w)); } while (w.qlock >= 0);
}
/**
- * Tries to enqueue worker w in wait queue and await change in
- * worker's eventCount. If the pool is quiescent and there is
- * more than one worker, possibly terminates worker upon exit.
- * Otherwise, before blocking, rescans queues to avoid missed
- * signals. Upon finding work, releases at least one worker
- * (which may be the current worker). Rescans restart upon
- * detected staleness or failure to release due to
- * contention. Note the unusual conventions about Thread.interrupt
- * here and elsewhere: Because interrupts are used solely to alert
- * threads to check termination, which is checked here anyway, we
- * clear status (using Thread.interrupted) before any call to
- * park, so that park does not immediately return due to status
- * being set via some other unrelated call to interrupt in user
- * code.
+ * Scans for and, if found, returns one task, else possibly
+ * inactivates the worker. This method operates on single reads of
+ * volatile state and is designed to be re-invoked continuously,
+ * in part because it returns upon detecting inconsistencies,
+ * contention, or state changes that indicate possible success on
+ * re-invocation.
*
- * @param w the calling worker
- * @param c the ctl value on entry
- * @return true if waited or another thread was released upon enq
+ * The scan searches for tasks across queues (starting at a random
+ * index, and relying on registerWorker to irregularly scatter
+ * them within array to avoid bias), checking each at least twice.
+ * The scan terminates upon either finding a non-empty queue, or
+ * completing the sweep. If the worker is not inactivated, it
+ * takes and returns a task from this queue. Otherwise, if not
+ * activated, it signals workers (that may include itself) and
+ * returns so caller can retry. Also returns for true if the
+ * worker array may have changed during an empty scan. On failure
+ * to find a task, we take one of the following actions, after
+ * which the caller will retry calling this method unless
+ * terminated.
+ *
+ * * If pool is terminating, terminate the worker.
+ *
+ * * If not already enqueued, try to inactivate and enqueue the
+ * worker on wait queue. Or, if inactivating has caused the pool
+ * to be quiescent, relay to idleAwaitWork to possibly shrink
+ * pool.
+ *
+ * * If already enqueued and none of the above apply, possibly
+ * park awaiting signal, else lingering to help scan and signal.
+ *
+ * * If a non-empty queue discovered or left as a hint,
+ * help wake up other workers before return
+ *
+ * @param w the worker (via its WorkQueue)
+ * @return a task or null if none found
*/
- private boolean tryAwaitWork(ForkJoinWorkerThread w, long c) {
- int v = w.eventCount;
- w.nextWait = (int)c; // w's successor record
- long nc = (long)(v & E_MASK) | ((c - AC_UNIT) & (AC_MASK|TC_MASK));
- if (ctl != c || !UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
- long d = ctl; // return true if lost to a deq, to force scan
- return (int)d != (int)c && ((d - c) & AC_MASK) >= 0L;
- }
- for (int sc = w.stealCount; sc != 0;) { // accumulate stealCount
- long s = stealCount;
- if (UNSAFE.compareAndSwapLong(this, stealCountOffset, s, s + sc))
- sc = w.stealCount = 0;
- else if (w.eventCount != v)
- return true; // update next time
- }
- if ((!shutdown || !tryTerminate(false)) &&
- (int)c != 0 && parallelism + (int)(nc >> AC_SHIFT) == 0 &&
- blockedCount == 0 && quiescerCount == 0)
- idleAwaitWork(w, nc, c, v); // quiescent
- for (boolean rescanned = false;;) {
- if (w.eventCount != v)
- return true;
- if (!rescanned) {
- int g = scanGuard, m = g & SMASK;
- ForkJoinWorkerThread[] ws = workers;
- if (ws != null && m < ws.length) {
- rescanned = true;
- for (int i = 0; i <= m; ++i) {
- ForkJoinWorkerThread u = ws[i];
- if (u != null) {
- if (u.queueBase != u.queueTop &&
- !tryReleaseWaiter())
- rescanned = false; // contended
- if (w.eventCount != v)
- return true;
- }
+ private final ForkJoinTask<?> scan(WorkQueue w) {
+ WorkQueue[] ws; int m;
+ int ps = plock; // read plock before ws
+ if (w != null && (ws = workQueues) != null && (m = ws.length - 1) >= 0) {
+ int ec = w.eventCount; // ec is negative if inactive
+ int r = w.seed; r ^= r << 13; r ^= r >>> 17; w.seed = r ^= r << 5;
+ w.hint = -1; // update seed and clear hint
+ int j = ((m + m + 1) | MIN_SCAN) & MAX_SCAN;
+ do {
+ WorkQueue q; ForkJoinTask<?>[] a; int b;
+ if ((q = ws[(r + j) & m]) != null && (b = q.base) - q.top < 0 &&
+ (a = q.array) != null) { // probably nonempty
+ int i = (((a.length - 1) & b) << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, i);
+ if (q.base == b && ec >= 0 && t != null &&
+ U.compareAndSwapObject(a, i, t, null)) {
+ if ((q.base = b + 1) - q.top < 0)
+ signalWork(q);
+ return t; // taken
+ }
+ else if ((ec < 0 || j < m) && (int)(ctl >> AC_SHIFT) <= 0) {
+ w.hint = (r + j) & m; // help signal below
+ break; // cannot take
}
}
- if (scanGuard != g || // stale
- (queueBase != queueTop && !tryReleaseWaiter()))
- rescanned = false;
- if (!rescanned)
- Thread.yield(); // reduce contention
- else
- Thread.interrupted(); // clear before park
+ } while (--j >= 0);
+
+ int h, e, ns; long c, sc; WorkQueue q;
+ if ((ns = w.nsteals) != 0) {
+ if (U.compareAndSwapLong(this, STEALCOUNT,
+ sc = stealCount, sc + ns))
+ w.nsteals = 0; // collect steals and rescan
}
+ else if (plock != ps) // consistency check
+ ; // skip
+ else if ((e = (int)(c = ctl)) < 0)
+ w.qlock = -1; // pool is terminating
else {
- w.parked = true; // must recheck
- if (w.eventCount != v) {
- w.parked = false;
- return true;
+ if ((h = w.hint) < 0) {
+ if (ec >= 0) { // try to enqueue/inactivate
+ long nc = (((long)ec |
+ ((c - AC_UNIT) & (AC_MASK|TC_MASK))));
+ w.nextWait = e; // link and mark inactive
+ w.eventCount = ec | INT_SIGN;
+ if (ctl != c || !U.compareAndSwapLong(this, CTL, c, nc))
+ w.eventCount = ec; // unmark on CAS failure
+ else if ((int)(c >> AC_SHIFT) == 1 - (config & SMASK))
+ idleAwaitWork(w, nc, c);
+ }
+ else if (w.eventCount < 0 && !tryTerminate(false, false) &&
+ ctl == c) { // block
+ Thread wt = Thread.currentThread();
+ Thread.interrupted(); // clear status
+ U.putObject(wt, PARKBLOCKER, this);
+ w.parker = wt; // emulate LockSupport.park
+ if (w.eventCount < 0) // recheck
+ U.park(false, 0L);
+ w.parker = null;
+ U.putObject(wt, PARKBLOCKER, null);
+ }
}
- LockSupport.park(this);
- rescanned = w.parked = false;
+ if ((h >= 0 || (h = w.hint) >= 0) &&
+ (ws = workQueues) != null && h < ws.length &&
+ (q = ws[h]) != null) { // signal others before retry
+ WorkQueue v; Thread p; int u, i, s;
+ for (int n = (config & SMASK) >>> 1;;) {
+ int idleCount = (w.eventCount < 0) ? 0 : -1;
+ if (((s = idleCount - q.base + q.top) <= n &&
+ (n = s) <= 0) ||
+ (u = (int)((c = ctl) >>> 32)) >= 0 ||
+ (e = (int)c) <= 0 || m < (i = e & SMASK) ||
+ (v = ws[i]) == null)
+ break;
+ long nc = (((long)(v.nextWait & E_MASK)) |
+ ((long)(u + UAC_UNIT) << 32));
+ if (v.eventCount != (e | INT_SIGN) ||
+ !U.compareAndSwapLong(this, CTL, c, nc))
+ break;
+ v.hint = h;
+ v.eventCount = (e + E_SEQ) & E_MASK;
+ if ((p = v.parker) != null)
+ U.unpark(p);
+ if (--n <= 0)
+ break;
+ }
+ }
}
}
+ return null;
}
/**
- * If inactivating worker w has caused pool to become
- * quiescent, check for pool termination, and wait for event
- * for up to SHRINK_RATE nanosecs (rescans are unnecessary in
- * this case because quiescence reflects consensus about lack
- * of work). On timeout, if ctl has not changed, terminate the
- * worker. Upon its termination (see deregisterWorker), it may
- * wake up another worker to possibly repeat this process.
+ * If inactivating worker w has caused the pool to become
+ * quiescent, checks for pool termination, and, so long as this is
+ * not the only worker, waits for event for up to a given
+ * duration. On timeout, if ctl has not changed, terminates the
+ * worker, which will in turn wake up another worker to possibly
+ * repeat this process.
*
* @param w the calling worker
- * @param currentCtl the ctl value after enqueuing w
- * @param prevCtl the ctl value if w terminated
- * @param v the eventCount w awaits change
+ * @param currentCtl the ctl value triggering possible quiescence
+ * @param prevCtl the ctl value to restore if thread is terminated
*/
- private void idleAwaitWork(ForkJoinWorkerThread w, long currentCtl,
- long prevCtl, int v) {
- if (w.eventCount == v) {
- if (shutdown)
- tryTerminate(false);
- ForkJoinTask.helpExpungeStaleExceptions(); // help clean weak refs
+ private void idleAwaitWork(WorkQueue w, long currentCtl, long prevCtl) {
+ if (w != null && w.eventCount < 0 &&
+ !tryTerminate(false, false) && (int)prevCtl != 0) {
+ int dc = -(short)(currentCtl >>> TC_SHIFT);
+ long parkTime = dc < 0 ? FAST_IDLE_TIMEOUT: (dc + 1) * IDLE_TIMEOUT;
+ long deadline = System.nanoTime() + parkTime - TIMEOUT_SLOP;
+ Thread wt = Thread.currentThread();
while (ctl == currentCtl) {
- long startTime = System.nanoTime();
- w.parked = true;
- if (w.eventCount == v) // must recheck
- LockSupport.parkNanos(this, SHRINK_RATE);
- w.parked = false;
- if (w.eventCount != v)
+ Thread.interrupted(); // timed variant of version in scan()
+ U.putObject(wt, PARKBLOCKER, this);
+ w.parker = wt;
+ if (ctl == currentCtl)
+ U.park(false, parkTime);
+ w.parker = null;
+ U.putObject(wt, PARKBLOCKER, null);
+ if (ctl != currentCtl)
break;
- else if (System.nanoTime() - startTime <
- SHRINK_RATE - (SHRINK_RATE / 10)) // timing slop
- Thread.interrupted(); // spurious wakeup
- else if (UNSAFE.compareAndSwapLong(this, ctlOffset,
- currentCtl, prevCtl)) {
- w.terminate = true; // restore previous
- w.eventCount = ((int)currentCtl + EC_UNIT) & E_MASK;
+ if (deadline - System.nanoTime() <= 0L &&
+ U.compareAndSwapLong(this, CTL, currentCtl, prevCtl)) {
+ w.eventCount = (w.eventCount + E_SEQ) | E_MASK;
+ w.qlock = -1; // shrink
break;
}
}
}
}
- // Submissions
-
/**
- * Enqueues the given task in the submissionQueue. Same idea as
- * ForkJoinWorkerThread.pushTask except for use of submissionLock.
- *
- * @param t the task
- */
- private void addSubmission(ForkJoinTask<?> t) {
- final ReentrantLock lock = this.submissionLock;
- lock.lock();
- try {
- ForkJoinTask<?>[] q; int s, m;
- if ((q = submissionQueue) != null) { // ignore if queue removed
- long u = (((s = queueTop) & (m = q.length-1)) << ASHIFT)+ABASE;
- UNSAFE.putOrderedObject(q, u, t);
- queueTop = s + 1;
- if (s - queueBase == m)
- growSubmissionQueue();
- }
- } finally {
- lock.unlock();
- }
- signalWork();
- }
-
- // (pollSubmission is defined below with exported methods)
-
- /**
- * Creates or doubles submissionQueue array.
- * Basically identical to ForkJoinWorkerThread version.
- */
- private void growSubmissionQueue() {
- ForkJoinTask<?>[] oldQ = submissionQueue;
- int size = oldQ != null ? oldQ.length << 1 : INITIAL_QUEUE_CAPACITY;
- if (size > MAXIMUM_QUEUE_CAPACITY)
- throw new RejectedExecutionException("Queue capacity exceeded");
- if (size < INITIAL_QUEUE_CAPACITY)
- size = INITIAL_QUEUE_CAPACITY;
- ForkJoinTask<?>[] q = submissionQueue = new ForkJoinTask<?>[size];
- int mask = size - 1;
- int top = queueTop;
- int oldMask;
- if (oldQ != null && (oldMask = oldQ.length - 1) >= 0) {
- for (int b = queueBase; b != top; ++b) {
- long u = ((b & oldMask) << ASHIFT) + ABASE;
- Object x = UNSAFE.getObjectVolatile(oldQ, u);
- if (x != null && UNSAFE.compareAndSwapObject(oldQ, u, x, null))
- UNSAFE.putObjectVolatile
- (q, ((b & mask) << ASHIFT) + ABASE, x);
- }
- }
- }
-
- // Blocking support
-
- /**
- * Tries to increment blockedCount, decrement active count
- * (sometimes implicitly) and possibly release or create a
- * compensating worker in preparation for blocking. Fails
- * on contention or termination.
+ * Scans through queues looking for work while joining a task; if
+ * any present, signals. May return early if more signalling is
+ * detectably unneeded.
*
- * @return true if the caller can block, else should recheck and retry
- */
- private boolean tryPreBlock() {
- int b = blockedCount;
- if (UNSAFE.compareAndSwapInt(this, blockedCountOffset, b, b + 1)) {
- int pc = parallelism;
- do {
- ForkJoinWorkerThread[] ws; ForkJoinWorkerThread w;
- int e, ac, tc, rc, i;
- long c = ctl;
- int u = (int)(c >>> 32);
- if ((e = (int)c) < 0) {
- // skip -- terminating
- }
- else if ((ac = (u >> UAC_SHIFT)) <= 0 && e != 0 &&
- (ws = workers) != null &&
- (i = ~e & SMASK) < ws.length &&
- (w = ws[i]) != null) {
- long nc = ((long)(w.nextWait & E_MASK) |
- (c & (AC_MASK|TC_MASK)));
- if (w.eventCount == e &&
- UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
- w.eventCount = (e + EC_UNIT) & E_MASK;
- if (w.parked)
- UNSAFE.unpark(w);
- return true; // release an idle worker
- }
- }
- else if ((tc = (short)(u >>> UTC_SHIFT)) >= 0 && ac + pc > 1) {
- long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK);
- if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc))
- return true; // no compensation needed
- }
- else if (tc + pc < MAX_ID) {
- long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK);
- if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
- addWorker();
- return true; // create a replacement
- }
- }
- // try to back out on any failure and let caller retry
- } while (!UNSAFE.compareAndSwapInt(this, blockedCountOffset,
- b = blockedCount, b - 1));
- }
- return false;
- }
-
- /**
- * Decrements blockedCount and increments active count
- */
- private void postBlock() {
- long c;
- do {} while (!UNSAFE.compareAndSwapLong(this, ctlOffset, // no mask
- c = ctl, c + AC_UNIT));
- int b;
- do {} while (!UNSAFE.compareAndSwapInt(this, blockedCountOffset,
- b = blockedCount, b - 1));
- }
-
- /**
- * Possibly blocks waiting for the given task to complete, or
- * cancels the task if terminating. Fails to wait if contended.
- *
- * @param joinMe the task
+ * @param task return early if done
+ * @param origin an index to start scan
*/
- final void tryAwaitJoin(ForkJoinTask<?> joinMe) {
- int s;
- Thread.interrupted(); // clear interrupts before checking termination
- if (joinMe.status >= 0) {
- if (tryPreBlock()) {
- joinMe.tryAwaitDone(0L);
- postBlock();
- }
- else if ((ctl & STOP_BIT) != 0L)
- joinMe.cancelIgnoringExceptions();
- }
- }
-
- /**
- * Possibly blocks the given worker waiting for joinMe to
- * complete or timeout
- *
- * @param joinMe the task
- * @param millis the wait time for underlying Object.wait
- */
- final void timedAwaitJoin(ForkJoinTask<?> joinMe, long nanos) {
- while (joinMe.status >= 0) {
- Thread.interrupted();
- if ((ctl & STOP_BIT) != 0L) {
- joinMe.cancelIgnoringExceptions();
- break;
- }
- if (tryPreBlock()) {
- long last = System.nanoTime();
- while (joinMe.status >= 0) {
- long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
- if (millis <= 0)
- break;
- joinMe.tryAwaitDone(millis);
- if (joinMe.status < 0)
- break;
- if ((ctl & STOP_BIT) != 0L) {
- joinMe.cancelIgnoringExceptions();
+ private void helpSignal(ForkJoinTask<?> task, int origin) {
+ WorkQueue[] ws; WorkQueue w; Thread p; long c; int m, u, e, i, s;
+ if (task != null && task.status >= 0 &&
+ (u = (int)(ctl >>> 32)) < 0 && (u >> UAC_SHIFT) < 0 &&
+ (ws = workQueues) != null && (m = ws.length - 1) >= 0) {
+ outer: for (int k = origin, j = m; j >= 0; --j) {
+ WorkQueue q = ws[k++ & m];
+ for (int n = m;;) { // limit to at most m signals
+ if (task.status < 0)
+ break outer;
+ if (q == null ||
+ ((s = -q.base + q.top) <= n && (n = s) <= 0))
break;
- }
- long now = System.nanoTime();
- nanos -= now - last;
- last = now;
- }
- postBlock();
- break;
- }
- }
- }
-
- /**
- * If necessary, compensates for blocker, and blocks
- */
- private void awaitBlocker(ManagedBlocker blocker)
- throws InterruptedException {
- while (!blocker.isReleasable()) {
- if (tryPreBlock()) {
- try {
- do {} while (!blocker.isReleasable() && !blocker.block());
- } finally {
- postBlock();
- }
- break;
- }
- }
- }
-
- // Creating, registering and deregistring workers
-
- /**
- * Tries to create and start a worker; minimally rolls back counts
- * on failure.
- */
- private void addWorker() {
- Throwable ex = null;
- ForkJoinWorkerThread t = null;
- try {
- t = factory.newThread(this);
- } catch (Throwable e) {
- ex = e;
- }
- if (t == null) { // null or exceptional factory return
- long c; // adjust counts
- do {} while (!UNSAFE.compareAndSwapLong
- (this, ctlOffset, c = ctl,
- (((c - AC_UNIT) & AC_MASK) |
- ((c - TC_UNIT) & TC_MASK) |
- (c & ~(AC_MASK|TC_MASK)))));
- // Propagate exception if originating from an external caller
- if (!tryTerminate(false) && ex != null &&
- !(Thread.currentThread() instanceof ForkJoinWorkerThread))
- UNSAFE.throwException(ex);
- }
- else
- t.start();
- }
-
- /**
- * Callback from ForkJoinWorkerThread constructor to assign a
- * public name
- */
- final String nextWorkerName() {
- for (int n;;) {
- if (UNSAFE.compareAndSwapInt(this, nextWorkerNumberOffset,
- n = nextWorkerNumber, ++n))
- return workerNamePrefix + n;
- }
- }
-
- /**
- * Callback from ForkJoinWorkerThread constructor to
- * determine its poolIndex and record in workers array.
- *
- * @param w the worker
- * @return the worker's pool index
- */
- final int registerWorker(ForkJoinWorkerThread w) {
- /*
- * In the typical case, a new worker acquires the lock, uses
- * next available index and returns quickly. Since we should
- * not block callers (ultimately from signalWork or
- * tryPreBlock) waiting for the lock needed to do this, we
- * instead help release other workers while waiting for the
- * lock.
- */
- for (int g;;) {
- ForkJoinWorkerThread[] ws;
- if (((g = scanGuard) & SG_UNIT) == 0 &&
- UNSAFE.compareAndSwapInt(this, scanGuardOffset,
- g, g | SG_UNIT)) {
- int k = nextWorkerIndex;
- try {
- if ((ws = workers) != null) { // ignore on shutdown
- int n = ws.length;
- if (k < 0 || k >= n || ws[k] != null) {
- for (k = 0; k < n && ws[k] != null; ++k)
- ;
- if (k == n)
- ws = workers = Arrays.copyOf(ws, n << 1);
- }
- ws[k] = w;
- nextWorkerIndex = k + 1;
- int m = g & SMASK;
- g = (k > m) ? ((m << 1) + 1) & SMASK : g + (SG_UNIT<<1);
- }
- } finally {
- scanGuard = g;
- }
- return k;
- }
- else if ((ws = workers) != null) { // help release others
- for (ForkJoinWorkerThread u : ws) {
- if (u != null && u.queueBase != u.queueTop) {
- if (tryReleaseWaiter())
+ if ((u = (int)((c = ctl) >>> 32)) >= 0 ||
+ (e = (int)c) <= 0 || m < (i = e & SMASK) ||
+ (w = ws[i]) == null)
+ break outer;
+ long nc = (((long)(w.nextWait & E_MASK)) |
+ ((long)(u + UAC_UNIT) << 32));
+ if (w.eventCount != (e | INT_SIGN))
+ break outer;
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ w.eventCount = (e + E_SEQ) & E_MASK;
+ if ((p = w.parker) != null)
+ U.unpark(p);
+ if (--n <= 0)
break;
}
}
@@ -1197,202 +1881,631 @@
}
/**
- * Final callback from terminating worker. Removes record of
- * worker from array, and adjusts counts. If pool is shutting
- * down, tries to complete termination.
- *
- * @param w the worker
- */
- final void deregisterWorker(ForkJoinWorkerThread w, Throwable ex) {
- int idx = w.poolIndex;
- int sc = w.stealCount;
- int steps = 0;
- // Remove from array, adjust worker counts and collect steal count.
- // We can intermix failed removes or adjusts with steal updates
- do {
- long s, c;
- int g;
- if (steps == 0 && ((g = scanGuard) & SG_UNIT) == 0 &&
- UNSAFE.compareAndSwapInt(this, scanGuardOffset,
- g, g |= SG_UNIT)) {
- ForkJoinWorkerThread[] ws = workers;
- if (ws != null && idx >= 0 &&
- idx < ws.length && ws[idx] == w)
- ws[idx] = null; // verify
- nextWorkerIndex = idx;
- scanGuard = g + SG_UNIT;
- steps = 1;
- }
- if (steps == 1 &&
- UNSAFE.compareAndSwapLong(this, ctlOffset, c = ctl,
- (((c - AC_UNIT) & AC_MASK) |
- ((c - TC_UNIT) & TC_MASK) |
- (c & ~(AC_MASK|TC_MASK)))))
- steps = 2;
- if (sc != 0 &&
- UNSAFE.compareAndSwapLong(this, stealCountOffset,
- s = stealCount, s + sc))
- sc = 0;
- } while (steps != 2 || sc != 0);
- if (!tryTerminate(false)) {
- if (ex != null) // possibly replace if died abnormally
- signalWork();
- else
- tryReleaseWaiter();
- }
- }
-
- // Shutdown and termination
-
- /**
- * Possibly initiates and/or completes termination.
+ * Tries to locate and execute tasks for a stealer of the given
+ * task, or in turn one of its stealers, Traces currentSteal ->
+ * currentJoin links looking for a thread working on a descendant
+ * of the given task and with a non-empty queue to steal back and
+ * execute tasks from. The first call to this method upon a
+ * waiting join will often entail scanning/search, (which is OK
+ * because the joiner has nothing better to do), but this method
+ * leaves hints in workers to speed up subsequent calls. The
+ * implementation is very branchy to cope with potential
+ * inconsistencies or loops encountering chains that are stale,
+ * unknown, or so long that they are likely cyclic.
*
- * @param now if true, unconditionally terminate, else only
- * if shutdown and empty queue and no active workers
- * @return true if now terminating or terminated
+ * @param joiner the joining worker
+ * @param task the task to join
+ * @return 0 if no progress can be made, negative if task
+ * known complete, else positive
*/
- private boolean tryTerminate(boolean now) {
- long c;
- while (((c = ctl) & STOP_BIT) == 0) {
- if (!now) {
- if ((int)(c >> AC_SHIFT) != -parallelism)
- return false;
- if (!shutdown || blockedCount != 0 || quiescerCount != 0 ||
- queueBase != queueTop) {
- if (ctl == c) // staleness check
- return false;
- continue;
- }
- }
- if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, c | STOP_BIT))
- startTerminating();
- }
- if ((short)(c >>> TC_SHIFT) == -parallelism) { // signal when 0 workers
- final ReentrantLock lock = this.submissionLock;
- lock.lock();
- try {
- termination.signalAll();
- } finally {
- lock.unlock();
- }
- }
- return true;
- }
-
- /**
- * Runs up to three passes through workers: (0) Setting
- * termination status for each worker, followed by wakeups up to
- * queued workers; (1) helping cancel tasks; (2) interrupting
- * lagging threads (likely in external tasks, but possibly also
- * blocked in joins). Each pass repeats previous steps because of
- * potential lagging thread creation.
- */
- private void startTerminating() {
- cancelSubmissions();
- for (int pass = 0; pass < 3; ++pass) {
- ForkJoinWorkerThread[] ws = workers;
- if (ws != null) {
- for (ForkJoinWorkerThread w : ws) {
- if (w != null) {
- w.terminate = true;
- if (pass > 0) {
- w.cancelTasks();
- if (pass > 1 && !w.isInterrupted()) {
- try {
- w.interrupt();
- } catch (SecurityException ignore) {
- }
+ private int tryHelpStealer(WorkQueue joiner, ForkJoinTask<?> task) {
+ int stat = 0, steps = 0; // bound to avoid cycles
+ if (joiner != null && task != null) { // hoist null checks
+ restart: for (;;) {
+ ForkJoinTask<?> subtask = task; // current target
+ for (WorkQueue j = joiner, v;;) { // v is stealer of subtask
+ WorkQueue[] ws; int m, s, h;
+ if ((s = task.status) < 0) {
+ stat = s;
+ break restart;
+ }
+ if ((ws = workQueues) == null || (m = ws.length - 1) <= 0)
+ break restart; // shutting down
+ if ((v = ws[h = (j.hint | 1) & m]) == null ||
+ v.currentSteal != subtask) {
+ for (int origin = h;;) { // find stealer
+ if (((h = (h + 2) & m) & 15) == 1 &&
+ (subtask.status < 0 || j.currentJoin != subtask))
+ continue restart; // occasional staleness check
+ if ((v = ws[h]) != null &&
+ v.currentSteal == subtask) {
+ j.hint = h; // save hint
+ break;
+ }
+ if (h == origin)
+ break restart; // cannot find stealer
+ }
+ }
+ for (;;) { // help stealer or descend to its stealer
+ ForkJoinTask[] a; int b;
+ if (subtask.status < 0) // surround probes with
+ continue restart; // consistency checks
+ if ((b = v.base) - v.top < 0 && (a = v.array) != null) {
+ int i = (((a.length - 1) & b) << ASHIFT) + ABASE;
+ ForkJoinTask<?> t =
+ (ForkJoinTask<?>)U.getObjectVolatile(a, i);
+ if (subtask.status < 0 || j.currentJoin != subtask ||
+ v.currentSteal != subtask)
+ continue restart; // stale
+ stat = 1; // apparent progress
+ if (t != null && v.base == b &&
+ U.compareAndSwapObject(a, i, t, null)) {
+ v.base = b + 1; // help stealer
+ joiner.runSubtask(t);
+ }
+ else if (v.base == b && ++steps == MAX_HELP)
+ break restart; // v apparently stalled
+ }
+ else { // empty -- try to descend
+ ForkJoinTask<?> next = v.currentJoin;
+ if (subtask.status < 0 || j.currentJoin != subtask ||
+ v.currentSteal != subtask)
+ continue restart; // stale
+ else if (next == null || ++steps == MAX_HELP)
+ break restart; // dead-end or maybe cyclic
+ else {
+ subtask = next;
+ j = v;
+ break;
}
}
}
}
- terminateWaiters();
+ }
+ }
+ return stat;
+ }
+
+ /**
+ * Analog of tryHelpStealer for CountedCompleters. Tries to steal
+ * and run tasks within the target's computation.
+ *
+ * @param task the task to join
+ * @param mode if shared, exit upon completing any task
+ * if all workers are active
+ *
+ */
+ private int helpComplete(ForkJoinTask<?> task, int mode) {
+ WorkQueue[] ws; WorkQueue q; int m, n, s, u;
+ if (task != null && (ws = workQueues) != null &&
+ (m = ws.length - 1) >= 0) {
+ for (int j = 1, origin = j;;) {
+ if ((s = task.status) < 0)
+ return s;
+ if ((q = ws[j & m]) != null && q.pollAndExecCC(task)) {
+ origin = j;
+ if (mode == SHARED_QUEUE &&
+ ((u = (int)(ctl >>> 32)) >= 0 || (u >> UAC_SHIFT) >= 0))
+ break;
+ }
+ else if ((j = (j + 2) & m) == origin)
+ break;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Tries to decrement active count (sometimes implicitly) and
+ * possibly release or create a compensating worker in preparation
+ * for blocking. Fails on contention or termination. Otherwise,
+ * adds a new thread if no idle workers are available and pool
+ * may become starved.
+ */
+ final boolean tryCompensate() {
+ int pc = config & SMASK, e, i, tc; long c;
+ WorkQueue[] ws; WorkQueue w; Thread p;
+ if ((ws = workQueues) != null && (e = (int)(c = ctl)) >= 0) {
+ if (e != 0 && (i = e & SMASK) < ws.length &&
+ (w = ws[i]) != null && w.eventCount == (e | INT_SIGN)) {
+ long nc = ((long)(w.nextWait & E_MASK) |
+ (c & (AC_MASK|TC_MASK)));
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ w.eventCount = (e + E_SEQ) & E_MASK;
+ if ((p = w.parker) != null)
+ U.unpark(p);
+ return true; // replace with idle worker
+ }
+ }
+ else if ((tc = (short)(c >>> TC_SHIFT)) >= 0 &&
+ (int)(c >> AC_SHIFT) + pc > 1) {
+ long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK);
+ if (U.compareAndSwapLong(this, CTL, c, nc))
+ return true; // no compensation
+ }
+ else if (tc + pc < MAX_CAP) {
+ long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK);
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ ForkJoinWorkerThreadFactory fac;
+ Throwable ex = null;
+ ForkJoinWorkerThread wt = null;
+ try {
+ if ((fac = factory) != null &&
+ (wt = fac.newThread(this)) != null) {
+ wt.start();
+ return true;
+ }
+ } catch (Throwable rex) {
+ ex = rex;
+ }
+ deregisterWorker(wt, ex); // clean up and return false
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Helps and/or blocks until the given task is done.
+ *
+ * @param joiner the joining worker
+ * @param task the task
+ * @return task status on exit
+ */
+ final int awaitJoin(WorkQueue joiner, ForkJoinTask<?> task) {
+ int s = 0;
+ if (joiner != null && task != null && (s = task.status) >= 0) {
+ ForkJoinTask<?> prevJoin = joiner.currentJoin;
+ joiner.currentJoin = task;
+ do {} while ((s = task.status) >= 0 && !joiner.isEmpty() &&
+ joiner.tryRemoveAndExec(task)); // process local tasks
+ if (s >= 0 && (s = task.status) >= 0) {
+ helpSignal(task, joiner.poolIndex);
+ if ((s = task.status) >= 0 &&
+ (task instanceof CountedCompleter))
+ s = helpComplete(task, LIFO_QUEUE);
+ }
+ while (s >= 0 && (s = task.status) >= 0) {
+ if ((!joiner.isEmpty() || // try helping
+ (s = tryHelpStealer(joiner, task)) == 0) &&
+ (s = task.status) >= 0) {
+ helpSignal(task, joiner.poolIndex);
+ if ((s = task.status) >= 0 && tryCompensate()) {
+ if (task.trySetSignal() && (s = task.status) >= 0) {
+ synchronized (task) {
+ if (task.status >= 0) {
+ try { // see ForkJoinTask
+ task.wait(); // for explanation
+ } catch (InterruptedException ie) {
+ }
+ }
+ else
+ task.notifyAll();
+ }
+ }
+ long c; // re-activate
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, c + AC_UNIT));
+ }
+ }
+ }
+ joiner.currentJoin = prevJoin;
+ }
+ return s;
+ }
+
+ /**
+ * Stripped-down variant of awaitJoin used by timed joins. Tries
+ * to help join only while there is continuous progress. (Caller
+ * will then enter a timed wait.)
+ *
+ * @param joiner the joining worker
+ * @param task the task
+ */
+ final void helpJoinOnce(WorkQueue joiner, ForkJoinTask<?> task) {
+ int s;
+ if (joiner != null && task != null && (s = task.status) >= 0) {
+ ForkJoinTask<?> prevJoin = joiner.currentJoin;
+ joiner.currentJoin = task;
+ do {} while ((s = task.status) >= 0 && !joiner.isEmpty() &&
+ joiner.tryRemoveAndExec(task));
+ if (s >= 0 && (s = task.status) >= 0) {
+ helpSignal(task, joiner.poolIndex);
+ if ((s = task.status) >= 0 &&
+ (task instanceof CountedCompleter))
+ s = helpComplete(task, LIFO_QUEUE);
+ }
+ if (s >= 0 && joiner.isEmpty()) {
+ do {} while (task.status >= 0 &&
+ tryHelpStealer(joiner, task) > 0);
+ }
+ joiner.currentJoin = prevJoin;
+ }
+ }
+
+ /**
+ * Returns a (probably) non-empty steal queue, if one is found
+ * during a random, then cyclic scan, else null. This method must
+ * be retried by caller if, by the time it tries to use the queue,
+ * it is empty.
+ * @param r a (random) seed for scanning
+ */
+ private WorkQueue findNonEmptyStealQueue(int r) {
+ for (WorkQueue[] ws;;) {
+ int ps = plock, m, n;
+ if ((ws = workQueues) == null || (m = ws.length - 1) < 1)
+ return null;
+ for (int j = (m + 1) << 2; ;) {
+ WorkQueue q = ws[(((r + j) << 1) | 1) & m];
+ if (q != null && (n = q.base - q.top) < 0) {
+ if (n < -1)
+ signalWork(q);
+ return q;
+ }
+ else if (--j < 0) {
+ if (plock == ps)
+ return null;
+ break;
+ }
}
}
}
/**
- * Polls and cancels all submissions. Called only during termination.
+ * Runs tasks until {@code isQuiescent()}. We piggyback on
+ * active count ctl maintenance, but rather than blocking
+ * when tasks cannot be found, we rescan until all others cannot
+ * find tasks either.
*/
- private void cancelSubmissions() {
- while (queueBase != queueTop) {
- ForkJoinTask<?> task = pollSubmission();
- if (task != null) {
- try {
- task.cancel(false);
- } catch (Throwable ignore) {
+ final void helpQuiescePool(WorkQueue w) {
+ for (boolean active = true;;) {
+ ForkJoinTask<?> localTask; // exhaust local queue
+ while ((localTask = w.nextLocalTask()) != null)
+ localTask.doExec();
+ // Similar to loop in scan(), but ignoring submissions
+ WorkQueue q = findNonEmptyStealQueue(w.nextSeed());
+ if (q != null) {
+ ForkJoinTask<?> t; int b;
+ if (!active) { // re-establish active count
+ long c;
+ active = true;
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, c + AC_UNIT));
+ }
+ if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
+ w.runSubtask(t);
+ }
+ else {
+ long c;
+ if (active) { // decrement active count without queuing
+ active = false;
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, c -= AC_UNIT));
+ }
+ else
+ c = ctl; // re-increment on exit
+ if ((int)(c >> AC_SHIFT) + (config & SMASK) == 0) {
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, c + AC_UNIT));
+ break;
}
}
}
}
/**
- * Tries to set the termination status of waiting workers, and
- * then wakes them up (after which they will terminate).
+ * Gets and removes a local or stolen task for the given worker.
+ *
+ * @return a task, if available
+ */
+ final ForkJoinTask<?> nextTaskFor(WorkQueue w) {
+ for (ForkJoinTask<?> t;;) {
+ WorkQueue q; int b;
+ if ((t = w.nextLocalTask()) != null)
+ return t;
+ if ((q = findNonEmptyStealQueue(w.nextSeed())) == null)
+ return null;
+ if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
+ return t;
+ }
+ }
+
+ /**
+ * Returns a cheap heuristic guide for task partitioning when
+ * programmers, frameworks, tools, or languages have little or no
+ * idea about task granularity. In essence by offering this
+ * method, we ask users only about tradeoffs in overhead vs
+ * expected throughput and its variance, rather than how finely to
+ * partition tasks.
+ *
+ * In a steady state strict (tree-structured) computation, each
+ * thread makes available for stealing enough tasks for other
+ * threads to remain active. Inductively, if all threads play by
+ * the same rules, each thread should make available only a
+ * constant number of tasks.
+ *
+ * The minimum useful constant is just 1. But using a value of 1
+ * would require immediate replenishment upon each steal to
+ * maintain enough tasks, which is infeasible. Further,
+ * partitionings/granularities of offered tasks should minimize
+ * steal rates, which in general means that threads nearer the top
+ * of computation tree should generate more than those nearer the
+ * bottom. In perfect steady state, each thread is at
+ * approximately the same level of computation tree. However,
+ * producing extra tasks amortizes the uncertainty of progress and
+ * diffusion assumptions.
+ *
+ * So, users will want to use values larger, but not much larger
+ * than 1 to both smooth over transient shortages and hedge
+ * against uneven progress; as traded off against the cost of
+ * extra task overhead. We leave the user to pick a threshold
+ * value to compare with the results of this call to guide
+ * decisions, but recommend values such as 3.
+ *
+ * When all threads are active, it is on average OK to estimate
+ * surplus strictly locally. In steady-state, if one thread is
+ * maintaining say 2 surplus tasks, then so are others. So we can
+ * just use estimated queue length. However, this strategy alone
+ * leads to serious mis-estimates in some non-steady-state
+ * conditions (ramp-up, ramp-down, other stalls). We can detect
+ * many of these by further considering the number of "idle"
+ * threads, that are known to have zero queued tasks, so
+ * compensate by a factor of (#idle/#active) threads.
+ *
+ * Note: The approximation of #busy workers as #active workers is
+ * not very good under current signalling scheme, and should be
+ * improved.
*/
- private void terminateWaiters() {
- ForkJoinWorkerThread[] ws = workers;
- if (ws != null) {
- ForkJoinWorkerThread w; long c; int i, e;
- int n = ws.length;
- while ((i = ~(e = (int)(c = ctl)) & SMASK) < n &&
- (w = ws[i]) != null && w.eventCount == (e & E_MASK)) {
- if (UNSAFE.compareAndSwapLong(this, ctlOffset, c,
- (long)(w.nextWait & E_MASK) |
- ((c + AC_UNIT) & AC_MASK) |
- (c & (TC_MASK|STOP_BIT)))) {
- w.terminate = true;
- w.eventCount = e + EC_UNIT;
- if (w.parked)
- UNSAFE.unpark(w);
+ static int getSurplusQueuedTaskCount() {
+ Thread t; ForkJoinWorkerThread wt; ForkJoinPool pool; WorkQueue q;
+ if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)) {
+ int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).config & SMASK;
+ int n = (q = wt.workQueue).top - q.base;
+ int a = (int)(pool.ctl >> AC_SHIFT) + p;
+ return n - (a > (p >>>= 1) ? 0 :
+ a > (p >>>= 1) ? 1 :
+ a > (p >>>= 1) ? 2 :
+ a > (p >>>= 1) ? 4 :
+ 8);
+ }
+ return 0;
+ }
+
+ // Termination
+
+ /**
+ * Possibly initiates and/or completes termination. The caller
+ * triggering termination runs three passes through workQueues:
+ * (0) Setting termination status, followed by wakeups of queued
+ * workers; (1) cancelling all tasks; (2) interrupting lagging
+ * threads (likely in external tasks, but possibly also blocked in
+ * joins). Each pass repeats previous steps because of potential
+ * lagging thread creation.
+ *
+ * @param now if true, unconditionally terminate, else only
+ * if no work and no active workers
+ * @param enable if true, enable shutdown when next possible
+ * @return true if now terminating or terminated
+ */
+ private boolean tryTerminate(boolean now, boolean enable) {
+ if (this == commonPool) // cannot shut down
+ return false;
+ for (long c;;) {
+ if (((c = ctl) & STOP_BIT) != 0) { // already terminating
+ if ((short)(c >>> TC_SHIFT) == -(config & SMASK)) {
+ synchronized (this) {
+ notifyAll(); // signal when 0 workers
+ }
+ }
+ return true;
+ }
+ if (plock >= 0) { // not yet enabled
+ int ps;
+ if (!enable)
+ return false;
+ if (((ps = plock) & PL_LOCK) != 0 ||
+ !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+ ps = acquirePlock();
+ if (!U.compareAndSwapInt(this, PLOCK, ps, SHUTDOWN))
+ releasePlock(SHUTDOWN);
+ }
+ if (!now) { // check if idle & no tasks
+ if ((int)(c >> AC_SHIFT) != -(config & SMASK) ||
+ hasQueuedSubmissions())
+ return false;
+ // Check for unqueued inactive workers. One pass suffices.
+ WorkQueue[] ws = workQueues; WorkQueue w;
+ if (ws != null) {
+ for (int i = 1; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null && w.eventCount >= 0)
+ return false;
+ }
+ }
+ }
+ if (U.compareAndSwapLong(this, CTL, c, c | STOP_BIT)) {
+ for (int pass = 0; pass < 3; ++pass) {
+ WorkQueue[] ws = workQueues;
+ if (ws != null) {
+ WorkQueue w; Thread wt;
+ int n = ws.length;
+ for (int i = 0; i < n; ++i) {
+ if ((w = ws[i]) != null) {
+ w.qlock = -1;
+ if (pass > 0) {
+ w.cancelAll();
+ if (pass > 1 && (wt = w.owner) != null) {
+ if (!wt.isInterrupted()) {
+ try {
+ wt.interrupt();
+ } catch (SecurityException ignore) {
+ }
+ }
+ U.unpark(wt);
+ }
+ }
+ }
+ }
+ // Wake up workers parked on event queue
+ int i, e; long cc; Thread p;
+ while ((e = (int)(cc = ctl) & E_MASK) != 0 &&
+ (i = e & SMASK) < n &&
+ (w = ws[i]) != null) {
+ long nc = ((long)(w.nextWait & E_MASK) |
+ ((cc + AC_UNIT) & AC_MASK) |
+ (cc & (TC_MASK|STOP_BIT)));
+ if (w.eventCount == (e | INT_SIGN) &&
+ U.compareAndSwapLong(this, CTL, cc, nc)) {
+ w.eventCount = (e + E_SEQ) & E_MASK;
+ w.qlock = -1;
+ if ((p = w.parker) != null)
+ U.unpark(p);
+ }
+ }
+ }
}
}
}
}
- // misc ForkJoinWorkerThread support
+ // external operations on common pool
+
+ /**
+ * Returns common pool queue for a thread that has submitted at
+ * least one task.
+ */
+ static WorkQueue commonSubmitterQueue() {
+ ForkJoinPool p; WorkQueue[] ws; int m; Submitter z;
+ return ((z = submitters.get()) != null &&
+ (p = commonPool) != null &&
+ (ws = p.workQueues) != null &&
+ (m = ws.length - 1) >= 0) ?
+ ws[m & z.seed & SQMASK] : null;
+ }
/**
- * Increment or decrement quiescerCount. Needed only to prevent
- * triggering shutdown if a worker is transiently inactive while
- * checking quiescence.
- *
- * @param delta 1 for increment, -1 for decrement
+ * Tries to pop the given task from submitter's queue in common pool.
*/
- final void addQuiescerCount(int delta) {
- int c;
- do {} while (!UNSAFE.compareAndSwapInt(this, quiescerCountOffset,
- c = quiescerCount, c + delta));
+ static boolean tryExternalUnpush(ForkJoinTask<?> t) {
+ ForkJoinPool p; WorkQueue[] ws; WorkQueue q; Submitter z;
+ ForkJoinTask<?>[] a; int m, s;
+ if (t != null &&
+ (z = submitters.get()) != null &&
+ (p = commonPool) != null &&
+ (ws = p.workQueues) != null &&
+ (m = ws.length - 1) >= 0 &&
+ (q = ws[m & z.seed & SQMASK]) != null &&
+ (s = q.top) != q.base &&
+ (a = q.array) != null) {
+ long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
+ if (U.getObject(a, j) == t &&
+ U.compareAndSwapInt(q, QLOCK, 0, 1)) {
+ if (q.array == a && q.top == s && // recheck
+ U.compareAndSwapObject(a, j, t, null)) {
+ q.top = s - 1;
+ q.qlock = 0;
+ return true;
+ }
+ q.qlock = 0;
+ }
+ }
+ return false;
}
/**
- * Directly increment or decrement active count without
- * queuing. This method is used to transiently assert inactivation
- * while checking quiescence.
- *
- * @param delta 1 for increment, -1 for decrement
+ * Tries to pop and run local tasks within the same computation
+ * as the given root. On failure, tries to help complete from
+ * other queues via helpComplete.
*/
- final void addActiveCount(int delta) {
- long d = delta < 0 ? -AC_UNIT : AC_UNIT;
- long c;
- do {} while (!UNSAFE.compareAndSwapLong(this, ctlOffset, c = ctl,
- ((c + d) & AC_MASK) |
- (c & ~AC_MASK)));
+ private void externalHelpComplete(WorkQueue q, ForkJoinTask<?> root) {
+ ForkJoinTask<?>[] a; int m;
+ if (q != null && (a = q.array) != null && (m = (a.length - 1)) >= 0 &&
+ root != null && root.status >= 0) {
+ for (;;) {
+ int s, u; Object o; CountedCompleter<?> task = null;
+ if ((s = q.top) - q.base > 0) {
+ long j = ((m & (s - 1)) << ASHIFT) + ABASE;
+ if ((o = U.getObject(a, j)) != null &&
+ (o instanceof CountedCompleter)) {
+ CountedCompleter<?> t = (CountedCompleter<?>)o, r = t;
+ do {
+ if (r == root) {
+ if (U.compareAndSwapInt(q, QLOCK, 0, 1)) {
+ if (q.array == a && q.top == s &&
+ U.compareAndSwapObject(a, j, t, null)) {
+ q.top = s - 1;
+ task = t;
+ }
+ q.qlock = 0;
+ }
+ break;
+ }
+ } while ((r = r.completer) != null);
+ }
+ }
+ if (task != null)
+ task.doExec();
+ if (root.status < 0 ||
+ (u = (int)(ctl >>> 32)) >= 0 || (u >> UAC_SHIFT) >= 0)
+ break;
+ if (task == null) {
+ helpSignal(root, q.poolIndex);
+ if (root.status >= 0)
+ helpComplete(root, SHARED_QUEUE);
+ break;
+ }
+ }
+ }
}
/**
- * Returns the approximate (non-atomic) number of idle threads per
- * active thread.
+ * Tries to help execute or signal availability of the given task
+ * from submitter's queue in common pool.
*/
- final int idlePerActive() {
- // Approximate at powers of two for small values, saturate past 4
- int p = parallelism;
- int a = p + (int)(ctl >> AC_SHIFT);
- return (a > (p >>>= 1) ? 0 :
- a > (p >>>= 1) ? 1 :
- a > (p >>>= 1) ? 2 :
- a > (p >>>= 1) ? 4 :
- 8);
+ static void externalHelpJoin(ForkJoinTask<?> t) {
+ // Some hard-to-avoid overlap with tryExternalUnpush
+ ForkJoinPool p; WorkQueue[] ws; WorkQueue q, w; Submitter z;
+ ForkJoinTask<?>[] a; int m, s, n;
+ if (t != null &&
+ (z = submitters.get()) != null &&
+ (p = commonPool) != null &&
+ (ws = p.workQueues) != null &&
+ (m = ws.length - 1) >= 0 &&
+ (q = ws[m & z.seed & SQMASK]) != null &&
+ (a = q.array) != null) {
+ int am = a.length - 1;
+ if ((s = q.top) != q.base) {
+ long j = ((am & (s - 1)) << ASHIFT) + ABASE;
+ if (U.getObject(a, j) == t &&
+ U.compareAndSwapInt(q, QLOCK, 0, 1)) {
+ if (q.array == a && q.top == s &&
+ U.compareAndSwapObject(a, j, t, null)) {
+ q.top = s - 1;
+ q.qlock = 0;
+ t.doExec();
+ }
+ else
+ q.qlock = 0;
+ }
+ }
+ if (t.status >= 0) {
+ if (t instanceof CountedCompleter)
+ p.externalHelpComplete(q, t);
+ else
+ p.helpSignal(t, q.poolIndex);
+ }
+ }
+ }
+
+ /**
+ * Restricted version of helpQuiescePool for external callers
+ */
+ static void externalHelpQuiescePool() {
+ ForkJoinPool p; ForkJoinTask<?> t; WorkQueue q; int b;
+ if ((p = commonPool) != null &&
+ (q = p.findNonEmptyStealQueue(1)) != null &&
+ (b = q.base) - q.top < 0 &&
+ (t = q.pollAt(b)) != null)
+ t.doExec();
}
// Exported methods
@@ -1464,31 +2577,46 @@
checkPermission();
if (factory == null)
throw new NullPointerException();
- if (parallelism <= 0 || parallelism > MAX_ID)
+ if (parallelism <= 0 || parallelism > MAX_CAP)
throw new IllegalArgumentException();
- this.parallelism = parallelism;
this.factory = factory;
this.ueh = handler;
- this.locallyFifo = asyncMode;
+ this.config = parallelism | (asyncMode ? (FIFO_QUEUE << 16) : 0);
long np = (long)(-parallelism); // offset ctl counts
this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
- this.submissionQueue = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY];
- // initialize workers array with room for 2*parallelism if possible
- int n = parallelism << 1;
- if (n >= MAX_ID)
- n = MAX_ID;
- else { // See Hackers Delight, sec 3.2, where n < (1 << 16)
- n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8;
- }
- workers = new ForkJoinWorkerThread[n + 1];
- this.submissionLock = new ReentrantLock();
- this.termination = submissionLock.newCondition();
+ int pn = nextPoolId();
StringBuilder sb = new StringBuilder("ForkJoinPool-");
- sb.append(poolNumberGenerator.incrementAndGet());
+ sb.append(Integer.toString(pn));
sb.append("-worker-");
this.workerNamePrefix = sb.toString();
}
+ /**
+ * Constructor for common pool, suitable only for static initialization.
+ * Basically the same as above, but uses smallest possible initial footprint.
+ */
+ ForkJoinPool(int parallelism, long ctl,
+ ForkJoinWorkerThreadFactory factory,
+ Thread.UncaughtExceptionHandler handler) {
+ this.config = parallelism;
+ this.ctl = ctl;
+ this.factory = factory;
+ this.ueh = handler;
+ this.workerNamePrefix = "ForkJoinPool.commonPool-worker-";
+ }
+
+ /**
+ * Returns the common pool instance. This pool is statically
+ * constructed; its run state is unaffected by attempts to
+ * {@link #shutdown} or {@link #shutdownNow}.
+ *
+ * @return the common pool instance
+ */
+ public static ForkJoinPool commonPool() {
+ // assert commonPool != null : "static init error";
+ return commonPool;
+ }
+
// Execution methods
/**
@@ -1508,34 +2636,10 @@
* scheduled for execution
*/
public <T> T invoke(ForkJoinTask<T> task) {
- Thread t = Thread.currentThread();
if (task == null)
throw new NullPointerException();
- if (shutdown)
- throw new RejectedExecutionException();
- if ((t instanceof ForkJoinWorkerThread) &&
- ((ForkJoinWorkerThread)t).pool == this)
- return task.invoke(); // bypass submit if in same pool
- else {
- addSubmission(task);
- return task.join();
- }
- }
-
- /**
- * Unless terminating, forks task if within an ongoing FJ
- * computation in the current pool, else submits as external task.
- */
- private <T> void forkOrSubmit(ForkJoinTask<T> task) {
- ForkJoinWorkerThread w;
- Thread t = Thread.currentThread();
- if (shutdown)
- throw new RejectedExecutionException();
- if ((t instanceof ForkJoinWorkerThread) &&
- (w = (ForkJoinWorkerThread)t).pool == this)
- w.pushTask(task);
- else
- addSubmission(task);
+ externalPush(task);
+ return task.join();
}
/**
@@ -1549,7 +2653,7 @@
public void execute(ForkJoinTask<?> task) {
if (task == null)
throw new NullPointerException();
- forkOrSubmit(task);
+ externalPush(task);
}
// AbstractExecutorService methods
@@ -1566,8 +2670,8 @@
if (task instanceof ForkJoinTask<?>) // avoid re-wrap
job = (ForkJoinTask<?>) task;
else
- job = ForkJoinTask.adapt(task, null);
- forkOrSubmit(job);
+ job = new ForkJoinTask.AdaptedRunnableAction(task);
+ externalPush(job);
}
/**
@@ -1582,7 +2686,7 @@
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
if (task == null)
throw new NullPointerException();
- forkOrSubmit(task);
+ externalPush(task);
return task;
}
@@ -1592,10 +2696,8 @@
* scheduled for execution
*/
public <T> ForkJoinTask<T> submit(Callable<T> task) {
- if (task == null)
- throw new NullPointerException();
- ForkJoinTask<T> job = ForkJoinTask.adapt(task);
- forkOrSubmit(job);
+ ForkJoinTask<T> job = new ForkJoinTask.AdaptedCallable<T>(task);
+ externalPush(job);
return job;
}
@@ -1605,10 +2707,8 @@
* scheduled for execution
*/
public <T> ForkJoinTask<T> submit(Runnable task, T result) {
- if (task == null)
- throw new NullPointerException();
- ForkJoinTask<T> job = ForkJoinTask.adapt(task, result);
- forkOrSubmit(job);
+ ForkJoinTask<T> job = new ForkJoinTask.AdaptedRunnable<T>(task, result);
+ externalPush(job);
return job;
}
@@ -1624,8 +2724,8 @@
if (task instanceof ForkJoinTask<?>) // avoid re-wrap
job = (ForkJoinTask<?>) task;
else
- job = ForkJoinTask.adapt(task, null);
- forkOrSubmit(job);
+ job = new ForkJoinTask.AdaptedRunnableAction(task);
+ externalPush(job);
return job;
}
@@ -1634,25 +2734,31 @@
* @throws RejectedExecutionException {@inheritDoc}
*/
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
- ArrayList<ForkJoinTask<T>> forkJoinTasks =
- new ArrayList<ForkJoinTask<T>>(tasks.size());
- for (Callable<T> task : tasks)
- forkJoinTasks.add(ForkJoinTask.adapt(task));
- invoke(new InvokeAll<T>(forkJoinTasks));
-
+ // In previous versions of this class, this method constructed
+ // a task to run ForkJoinTask.invokeAll, but now external
+ // invocation of multiple tasks is at least as efficient.
+ List<ForkJoinTask<T>> fs = new ArrayList<ForkJoinTask<T>>(tasks.size());
+ // Workaround needed because method wasn't declared with
+ // wildcards in return type but should have been.
@SuppressWarnings({"unchecked", "rawtypes"})
- List<Future<T>> futures = (List<Future<T>>) (List) forkJoinTasks;
- return futures;
- }
+ List<Future<T>> futures = (List<Future<T>>) (List) fs;
- static final class InvokeAll<T> extends RecursiveAction {
- final ArrayList<ForkJoinTask<T>> tasks;
- InvokeAll(ArrayList<ForkJoinTask<T>> tasks) { this.tasks = tasks; }
- public void compute() {
- try { invokeAll(tasks); }
- catch (Exception ignore) {}
+ boolean done = false;
+ try {
+ for (Callable<T> t : tasks) {
+ ForkJoinTask<T> f = new ForkJoinTask.AdaptedCallable<T>(t);
+ externalPush(f);
+ fs.add(f);
+ }
+ for (ForkJoinTask<T> f : fs)
+ f.quietlyJoin();
+ done = true;
+ return futures;
+ } finally {
+ if (!done)
+ for (ForkJoinTask<T> f : fs)
+ f.cancel(false);
}
- private static final long serialVersionUID = -7914297376763021607L;
}
/**
@@ -1680,7 +2786,16 @@
* @return the targeted parallelism level of this pool
*/
public int getParallelism() {
- return parallelism;
+ return config & SMASK;
+ }
+
+ /**
+ * Returns the targeted parallelism level of the common pool.
+ *
+ * @return the targeted parallelism level of the common pool
+ */
+ public static int getCommonPoolParallelism() {
+ return commonPoolParallelism;
}
/**
@@ -1692,7 +2807,7 @@
* @return the number of worker threads
*/
public int getPoolSize() {
- return parallelism + (short)(ctl >>> TC_SHIFT);
+ return (config & SMASK) + (short)(ctl >>> TC_SHIFT);
}
/**
@@ -1702,7 +2817,7 @@
* @return {@code true} if this pool uses async mode
*/
public boolean getAsyncMode() {
- return locallyFifo;
+ return (config >>> 16) == FIFO_QUEUE;
}
/**
@@ -1714,8 +2829,15 @@
* @return the number of worker threads
*/
public int getRunningThreadCount() {
- int r = parallelism + (int)(ctl >> AC_SHIFT);
- return (r <= 0) ? 0 : r; // suppress momentarily negative values
+ int rc = 0;
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 1; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null && w.isApparentlyUnblocked())
+ ++rc;
+ }
+ }
+ return rc;
}
/**
@@ -1726,7 +2848,7 @@
* @return the number of active threads
*/
public int getActiveThreadCount() {
- int r = parallelism + (int)(ctl >> AC_SHIFT) + blockedCount;
+ int r = (config & SMASK) + (int)(ctl >> AC_SHIFT);
return (r <= 0) ? 0 : r; // suppress momentarily negative values
}
@@ -1742,7 +2864,7 @@
* @return {@code true} if all threads are currently idle
*/
public boolean isQuiescent() {
- return parallelism + (int)(ctl >> AC_SHIFT) + blockedCount == 0;
+ return (int)(ctl >> AC_SHIFT) + (config & SMASK) == 0;
}
/**
@@ -1757,7 +2879,15 @@
* @return the number of steals
*/
public long getStealCount() {
- return stealCount;
+ long count = stealCount;
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 1; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null)
+ count += w.nsteals;
+ }
+ }
+ return count;
}
/**
@@ -1772,12 +2902,12 @@
*/
public long getQueuedTaskCount() {
long count = 0;
- ForkJoinWorkerThread[] ws;
- if ((short)(ctl >>> TC_SHIFT) > -parallelism &&
- (ws = workers) != null) {
- for (ForkJoinWorkerThread w : ws)
- if (w != null)
- count -= w.queueBase - w.queueTop; // must read base first
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 1; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null)
+ count += w.queueSize();
+ }
}
return count;
}
@@ -1790,7 +2920,15 @@
* @return the number of queued submissions
*/
public int getQueuedSubmissionCount() {
- return -queueBase + queueTop;
+ int count = 0;
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null)
+ count += w.queueSize();
+ }
+ }
+ return count;
}
/**
@@ -1800,7 +2938,14 @@
* @return {@code true} if there are any queued submissions
*/
public boolean hasQueuedSubmissions() {
- return queueBase != queueTop;
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null && !w.isEmpty())
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -1811,16 +2956,11 @@
* @return the next submission, or {@code null} if none
*/
protected ForkJoinTask<?> pollSubmission() {
- ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
- while ((b = queueBase) != queueTop &&
- (q = submissionQueue) != null &&
- (i = (q.length - 1) & b) >= 0) {
- long u = (i << ASHIFT) + ABASE;
- if ((t = q[i]) != null &&
- queueBase == b &&
- UNSAFE.compareAndSwapObject(q, u, t, null)) {
- queueBase = b + 1;
- return t;
+ WorkQueue[] ws; WorkQueue w; ForkJoinTask<?> t;
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null && (t = w.poll()) != null)
+ return t;
}
}
return null;
@@ -1845,20 +2985,17 @@
*/
protected int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
int count = 0;
- while (queueBase != queueTop) {
- ForkJoinTask<?> t = pollSubmission();
- if (t != null) {
- c.add(t);
- ++count;
+ WorkQueue[] ws; WorkQueue w; ForkJoinTask<?> t;
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; ++i) {
+ if ((w = ws[i]) != null) {
+ while ((t = w.poll()) != null) {
+ c.add(t);
+ ++count;
+ }
+ }
}
}
- ForkJoinWorkerThread[] ws;
- if ((short)(ctl >>> TC_SHIFT) > -parallelism &&
- (ws = workers) != null) {
- for (ForkJoinWorkerThread w : ws)
- if (w != null)
- count += w.drainTasksTo(c);
- }
return count;
}
@@ -1870,21 +3007,36 @@
* @return a string identifying this pool, as well as its state
*/
public String toString() {
- long st = getStealCount();
- long qt = getQueuedTaskCount();
- long qs = getQueuedSubmissionCount();
- int pc = parallelism;
+ // Use a single pass through workQueues to collect counts
+ long qt = 0L, qs = 0L; int rc = 0;
+ long st = stealCount;
long c = ctl;
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; ++i) {
+ if ((w = ws[i]) != null) {
+ int size = w.queueSize();
+ if ((i & 1) == 0)
+ qs += size;
+ else {
+ qt += size;
+ st += w.nsteals;
+ if (w.isApparentlyUnblocked())
+ ++rc;
+ }
+ }
+ }
+ }
+ int pc = (config & SMASK);
int tc = pc + (short)(c >>> TC_SHIFT);
- int rc = pc + (int)(c >> AC_SHIFT);
- if (rc < 0) // ignore transient negative
- rc = 0;
- int ac = rc + blockedCount;
+ int ac = pc + (int)(c >> AC_SHIFT);
+ if (ac < 0) // ignore transient negative
+ ac = 0;
String level;
if ((c & STOP_BIT) != 0)
level = (tc == 0) ? "Terminated" : "Terminating";
else
- level = shutdown ? "Shutting down" : "Running";
+ level = plock < 0 ? "Shutting down" : "Running";
return super.toString() +
"[" + level +
", parallelism = " + pc +
@@ -1898,11 +3050,13 @@
}
/**
- * Initiates an orderly shutdown in which previously submitted
- * tasks are executed, but no new tasks will be accepted.
- * Invocation has no additional effect if already shut down.
- * Tasks that are in the process of being submitted concurrently
- * during the course of this method may or may not be rejected.
+ * Possibly initiates an orderly shutdown in which previously
+ * submitted tasks are executed, but no new tasks will be
+ * accepted. Invocation has no effect on execution state if this
+ * is the {@link #commonPool}, and no additional effect if
+ * already shut down. Tasks that are in the process of being
+ * submitted concurrently during the course of this method may or
+ * may not be rejected.
*
* @throws SecurityException if a security manager exists and
* the caller is not permitted to modify threads
@@ -1911,19 +3065,20 @@
*/
public void shutdown() {
checkPermission();
- shutdown = true;
- tryTerminate(false);
+ tryTerminate(false, true);
}
/**
- * Attempts to cancel and/or stop all tasks, and reject all
- * subsequently submitted tasks. Tasks that are in the process of
- * being submitted or executed concurrently during the course of
- * this method may or may not be rejected. This method cancels
- * both existing and unexecuted tasks, in order to permit
- * termination in the presence of task dependencies. So the method
- * always returns an empty list (unlike the case for some other
- * Executors).
+ * Possibly attempts to cancel and/or stop all tasks, and reject
+ * all subsequently submitted tasks. Invocation has no effect on
+ * execution state if this is the {@link #commonPool}, and no
+ * additional effect if already shut down. Otherwise, tasks that
+ * are in the process of being submitted or executed concurrently
+ * during the course of this method may or may not be
+ * rejected. This method cancels both existing and unexecuted
+ * tasks, in order to permit termination in the presence of task
+ * dependencies. So the method always returns an empty list
+ * (unlike the case for some other Executors).
*
* @return an empty list
* @throws SecurityException if a security manager exists and
@@ -1933,8 +3088,7 @@
*/
public List<Runnable> shutdownNow() {
checkPermission();
- shutdown = true;
- tryTerminate(true);
+ tryTerminate(true, true);
return Collections.emptyList();
}
@@ -1946,7 +3100,7 @@
public boolean isTerminated() {
long c = ctl;
return ((c & STOP_BIT) != 0L &&
- (short)(c >>> TC_SHIFT) == -parallelism);
+ (short)(c >>> TC_SHIFT) == -(config & SMASK));
}
/**
@@ -1954,7 +3108,7 @@
* commenced but not yet completed. This method may be useful for
* debugging. A return of {@code true} reported a sufficient
* period after shutdown may indicate that submitted tasks have
- * ignored or suppressed interruption, or are waiting for IO,
+ * ignored or suppressed interruption, or are waiting for I/O,
* causing this executor not to properly terminate. (See the
* advisory notes for class {@link ForkJoinTask} stating that
* tasks should not normally entail blocking operations. But if
@@ -1965,14 +3119,7 @@
public boolean isTerminating() {
long c = ctl;
return ((c & STOP_BIT) != 0L &&
- (short)(c >>> TC_SHIFT) != -parallelism);
- }
-
- /**
- * Returns true if terminating or terminated. Used by ForkJoinWorkerThread.
- */
- final boolean isAtLeastTerminating() {
- return (ctl & STOP_BIT) != 0L;
+ (short)(c >>> TC_SHIFT) != -(config & SMASK));
}
/**
@@ -1981,13 +3128,15 @@
* @return {@code true} if this pool has been shut down
*/
public boolean isShutdown() {
- return shutdown;
+ return plock < 0;
}
/**
- * Blocks until all tasks have completed execution after a shutdown
- * request, or the timeout occurs, or the current thread is
- * interrupted, whichever happens first.
+ * Blocks until all tasks have completed execution after a
+ * shutdown request, or the timeout occurs, or the current thread
+ * is interrupted, whichever happens first. Note that the {@link
+ * #commonPool()} never terminates until program shutdown so
+ * this method will always time out.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
@@ -1998,19 +3147,21 @@
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
- final ReentrantLock lock = this.submissionLock;
- lock.lock();
- try {
- for (;;) {
- if (isTerminated())
- return true;
- if (nanos <= 0)
- return false;
- nanos = termination.awaitNanos(nanos);
+ if (isTerminated())
+ return true;
+ long startTime = System.nanoTime();
+ boolean terminated = false;
+ synchronized (this) {
+ for (long waitTime = nanos, millis = 0L;;) {
+ if (terminated = isTerminated() ||
+ waitTime <= 0L ||
+ (millis = unit.toMillis(waitTime)) <= 0L)
+ break;
+ wait(millis);
+ waitTime = nanos - (System.nanoTime() - startTime);
}
- } finally {
- lock.unlock();
}
+ return terminated;
}
/**
@@ -2110,11 +3261,35 @@
throws InterruptedException {
Thread t = Thread.currentThread();
if (t instanceof ForkJoinWorkerThread) {
- ForkJoinWorkerThread w = (ForkJoinWorkerThread) t;
- w.pool.awaitBlocker(blocker);
+ ForkJoinPool p = ((ForkJoinWorkerThread)t).pool;
+ while (!blocker.isReleasable()) { // variant of helpSignal
+ WorkQueue[] ws; WorkQueue q; int m, u;
+ if ((ws = p.workQueues) != null && (m = ws.length - 1) >= 0) {
+ for (int i = 0; i <= m; ++i) {
+ if (blocker.isReleasable())
+ return;
+ if ((q = ws[i]) != null && q.base - q.top < 0) {
+ p.signalWork(q);
+ if ((u = (int)(p.ctl >>> 32)) >= 0 ||
+ (u >> UAC_SHIFT) >= 0)
+ break;
+ }
+ }
+ }
+ if (p.tryCompensate()) {
+ try {
+ do {} while (!blocker.isReleasable() &&
+ !blocker.block());
+ } finally {
+ p.incrementActiveCount();
+ }
+ break;
+ }
+ }
}
else {
- do {} while (!blocker.isReleasable() && !blocker.block());
+ do {} while (!blocker.isReleasable() &&
+ !blocker.block());
}
}
@@ -2123,55 +3298,93 @@
// implement RunnableFuture.
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
- return (RunnableFuture<T>) ForkJoinTask.adapt(runnable, value);
+ return new ForkJoinTask.AdaptedRunnable<T>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
- return (RunnableFuture<T>) ForkJoinTask.adapt(callable);
+ return new ForkJoinTask.AdaptedCallable<T>(callable);
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long ctlOffset;
- private static final long stealCountOffset;
- private static final long blockedCountOffset;
- private static final long quiescerCountOffset;
- private static final long scanGuardOffset;
- private static final long nextWorkerNumberOffset;
- private static final long ABASE;
+ private static final sun.misc.Unsafe U;
+ private static final long CTL;
+ private static final long PARKBLOCKER;
+ private static final int ABASE;
private static final int ASHIFT;
+ private static final long STEALCOUNT;
+ private static final long PLOCK;
+ private static final long INDEXSEED;
+ private static final long QLOCK;
static {
- poolNumberGenerator = new AtomicInteger();
- workerSeedGenerator = new Random();
- modifyThreadPermission = new RuntimePermission("modifyThread");
- defaultForkJoinWorkerThreadFactory =
- new DefaultForkJoinWorkerThreadFactory();
- int s;
+ int s; // initialize field offsets for CAS etc
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
+ U = sun.misc.Unsafe.getUnsafe();
Class<?> k = ForkJoinPool.class;
- ctlOffset = UNSAFE.objectFieldOffset
+ CTL = U.objectFieldOffset
(k.getDeclaredField("ctl"));
- stealCountOffset = UNSAFE.objectFieldOffset
+ STEALCOUNT = U.objectFieldOffset
(k.getDeclaredField("stealCount"));
- blockedCountOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("blockedCount"));
- quiescerCountOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("quiescerCount"));
- scanGuardOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("scanGuard"));
- nextWorkerNumberOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("nextWorkerNumber"));
- Class<?> a = ForkJoinTask[].class;
- ABASE = UNSAFE.arrayBaseOffset(a);
- s = UNSAFE.arrayIndexScale(a);
+ PLOCK = U.objectFieldOffset
+ (k.getDeclaredField("plock"));
+ INDEXSEED = U.objectFieldOffset
+ (k.getDeclaredField("indexSeed"));
+ Class<?> tk = Thread.class;
+ PARKBLOCKER = U.objectFieldOffset
+ (tk.getDeclaredField("parkBlocker"));
+ Class<?> wk = WorkQueue.class;
+ QLOCK = U.objectFieldOffset
+ (wk.getDeclaredField("qlock"));
+ Class<?> ak = ForkJoinTask[].class;
+ ABASE = U.arrayBaseOffset(ak);
+ s = U.arrayIndexScale(ak);
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
} catch (Exception e) {
throw new Error(e);
}
if ((s & (s-1)) != 0)
throw new Error("data type scale not a power of two");
- ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
+
+ submitters = new ThreadLocal<Submitter>();
+ ForkJoinWorkerThreadFactory fac = defaultForkJoinWorkerThreadFactory =
+ new DefaultForkJoinWorkerThreadFactory();
+ modifyThreadPermission = new RuntimePermission("modifyThread");
+
+ /*
+ * Establish common pool parameters. For extra caution,
+ * computations to set up common pool state are here; the
+ * constructor just assigns these values to fields.
+ */
+
+ int par = 0;
+ Thread.UncaughtExceptionHandler handler = null;
+ try { // TBD: limit or report ignored exceptions?
+ String pp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.parallelism");
+ String hp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.exceptionHandler");
+ String fp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.threadFactory");
+ if (fp != null)
+ fac = ((ForkJoinWorkerThreadFactory)ClassLoader.
+ getSystemClassLoader().loadClass(fp).newInstance());
+ if (hp != null)
+ handler = ((Thread.UncaughtExceptionHandler)ClassLoader.
+ getSystemClassLoader().loadClass(hp).newInstance());
+ if (pp != null)
+ par = Integer.parseInt(pp);
+ } catch (Exception ignore) {
+ }
+
+ if (par <= 0)
+ par = Runtime.getRuntime().availableProcessors();
+ if (par > MAX_CAP)
+ par = MAX_CAP;
+ commonPoolParallelism = par;
+ long np = (long)(-par); // precompute initial ctl value
+ long ct = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
+
+ commonPool = new ForkJoinPool(par, ct, fac, handler);
}
}
--- a/jdk/src/share/classes/java/util/concurrent/ForkJoinTask.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinTask.java Fri Dec 28 18:36:41 2012 -0800
@@ -37,17 +37,13 @@
import java.io.Serializable;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;
-import java.util.Map;
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RunnableFuture;
@@ -63,46 +59,59 @@
* subtasks may be hosted by a small number of actual threads in a
* ForkJoinPool, at the price of some usage limitations.
*
- * <p>A "main" {@code ForkJoinTask} begins execution when submitted
- * to a {@link ForkJoinPool}. Once started, it will usually in turn
- * start other subtasks. As indicated by the name of this class,
- * many programs using {@code ForkJoinTask} employ only methods
- * {@link #fork} and {@link #join}, or derivatives such as {@link
+ * <p>A "main" {@code ForkJoinTask} begins execution when it is
+ * explicitly submitted to a {@link ForkJoinPool}, or, if not already
+ * engaged in a ForkJoin computation, commenced in the {@link
+ * ForkJoinPool#commonPool()} via {@link #fork}, {@link #invoke}, or
+ * related methods. Once started, it will usually in turn start other
+ * subtasks. As indicated by the name of this class, many programs
+ * using {@code ForkJoinTask} employ only methods {@link #fork} and
+ * {@link #join}, or derivatives such as {@link
* #invokeAll(ForkJoinTask...) invokeAll}. However, this class also
* provides a number of other methods that can come into play in
- * advanced usages, as well as extension mechanics that allow
- * support of new forms of fork/join processing.
+ * advanced usages, as well as extension mechanics that allow support
+ * of new forms of fork/join processing.
*
* <p>A {@code ForkJoinTask} is a lightweight form of {@link Future}.
* The efficiency of {@code ForkJoinTask}s stems from a set of
* restrictions (that are only partially statically enforceable)
- * reflecting their intended use as computational tasks calculating
- * pure functions or operating on purely isolated objects. The
- * primary coordination mechanisms are {@link #fork}, that arranges
+ * reflecting their main use as computational tasks calculating pure
+ * functions or operating on purely isolated objects. The primary
+ * coordination mechanisms are {@link #fork}, that arranges
* asynchronous execution, and {@link #join}, that doesn't proceed
* until the task's result has been computed. Computations should
- * avoid {@code synchronized} methods or blocks, and should minimize
- * other blocking synchronization apart from joining other tasks or
- * using synchronizers such as Phasers that are advertised to
- * cooperate with fork/join scheduling. Tasks should also not perform
- * blocking IO, and should ideally access variables that are
- * completely independent of those accessed by other running
- * tasks. Minor breaches of these restrictions, for example using
- * shared output streams, may be tolerable in practice, but frequent
- * use may result in poor performance, and the potential to
- * indefinitely stall if the number of threads not waiting for IO or
- * other external synchronization becomes exhausted. This usage
- * restriction is in part enforced by not permitting checked
- * exceptions such as {@code IOExceptions} to be thrown. However,
- * computations may still encounter unchecked exceptions, that are
- * rethrown to callers attempting to join them. These exceptions may
- * additionally include {@link RejectedExecutionException} stemming
- * from internal resource exhaustion, such as failure to allocate
- * internal task queues. Rethrown exceptions behave in the same way as
- * regular exceptions, but, when possible, contain stack traces (as
- * displayed for example using {@code ex.printStackTrace()}) of both
- * the thread that initiated the computation as well as the thread
- * actually encountering the exception; minimally only the latter.
+ * ideally avoid {@code synchronized} methods or blocks, and should
+ * minimize other blocking synchronization apart from joining other
+ * tasks or using synchronizers such as Phasers that are advertised to
+ * cooperate with fork/join scheduling. Subdividable tasks should also
+ * not perform blocking I/O, and should ideally access variables that
+ * are completely independent of those accessed by other running
+ * tasks. These guidelines are loosely enforced by not permitting
+ * checked exceptions such as {@code IOExceptions} to be
+ * thrown. However, computations may still encounter unchecked
+ * exceptions, that are rethrown to callers attempting to join
+ * them. These exceptions may additionally include {@link
+ * RejectedExecutionException} stemming from internal resource
+ * exhaustion, such as failure to allocate internal task
+ * queues. Rethrown exceptions behave in the same way as regular
+ * exceptions, but, when possible, contain stack traces (as displayed
+ * for example using {@code ex.printStackTrace()}) of both the thread
+ * that initiated the computation as well as the thread actually
+ * encountering the exception; minimally only the latter.
+ *
+ * <p>It is possible to define and use ForkJoinTasks that may block,
+ * but doing do requires three further considerations: (1) Completion
+ * of few if any <em>other</em> tasks should be dependent on a task
+ * that blocks on external synchronization or I/O. Event-style async
+ * tasks that are never joined (for example, those subclassing {@link
+ * CountedCompleter}) often fall into this category. (2) To minimize
+ * resource impact, tasks should be small; ideally performing only the
+ * (possibly) blocking action. (3) Unless the {@link
+ * ForkJoinPool.ManagedBlocker} API is used, or the number of possibly
+ * blocked tasks is known to be less than the pool's {@link
+ * ForkJoinPool#getParallelism} level, the pool cannot guarantee that
+ * enough threads will be available to ensure progress or good
+ * performance.
*
* <p>The primary method for awaiting completion and extracting
* results of a task is {@link #join}, but there are several variants:
@@ -118,6 +127,13 @@
* performs the most common form of parallel invocation: forking a set
* of tasks and joining them all.
*
+ * <p>In the most typical usages, a fork-join pair act like a call
+ * (fork) and return (join) from a parallel recursive function. As is
+ * the case with other forms of recursive calls, returns (joins)
+ * should be performed innermost-first. For example, {@code a.fork();
+ * b.fork(); b.join(); a.join();} is likely to be substantially more
+ * efficient than joining {@code a} before {@code b}.
+ *
* <p>The execution status of tasks may be queried at several levels
* of detail: {@link #isDone} is true if a task completed in any way
* (including the case where a task was cancelled without executing);
@@ -133,18 +149,13 @@
* <p>The ForkJoinTask class is not usually directly subclassed.
* Instead, you subclass one of the abstract classes that support a
* particular style of fork/join processing, typically {@link
- * RecursiveAction} for computations that do not return results, or
- * {@link RecursiveTask} for those that do. Normally, a concrete
- * ForkJoinTask subclass declares fields comprising its parameters,
- * established in a constructor, and then defines a {@code compute}
- * method that somehow uses the control methods supplied by this base
- * class. While these methods have {@code public} access (to allow
- * instances of different task subclasses to call each other's
- * methods), some of them may only be called from within other
- * ForkJoinTasks (as may be determined using method {@link
- * #inForkJoinPool}). Attempts to invoke them in other contexts
- * result in exceptions or errors, possibly including
- * {@code ClassCastException}.
+ * RecursiveAction} for most computations that do not return results,
+ * {@link RecursiveTask} for those that do, and {@link
+ * CountedCompleter} for those in which completed actions trigger
+ * other actions. Normally, a concrete ForkJoinTask subclass declares
+ * fields comprising its parameters, established in a constructor, and
+ * then defines a {@code compute} method that somehow uses the control
+ * methods supplied by this base class.
*
* <p>Method {@link #join} and its variants are appropriate for use
* only when completion dependencies are acyclic; that is, the
@@ -154,7 +165,17 @@
* supports other methods and techniques (for example the use of
* {@link Phaser}, {@link #helpQuiesce}, and {@link #complete}) that
* may be of use in constructing custom subclasses for problems that
- * are not statically structured as DAGs.
+ * are not statically structured as DAGs. To support such usages a
+ * ForkJoinTask may be atomically <em>tagged</em> with a {@code short}
+ * value using {@link #setForkJoinTaskTag} or {@link
+ * #compareAndSetForkJoinTaskTag} and checked using {@link
+ * #getForkJoinTaskTag}. The ForkJoinTask implementation does not use
+ * these {@code protected} methods or tags for any purpose, but they
+ * may be of use in the construction of specialized subclasses. For
+ * example, parallel graph traversals can use the supplied methods to
+ * avoid revisiting nodes/tasks that have already been processed.
+ * (Method names for tagging are bulky in part to encourage definition
+ * of methods that reflect their usage patterns.)
*
* <p>Most base support methods are {@code final}, to prevent
* overriding of implementations that are intrinsically tied to the
@@ -194,41 +215,50 @@
* See the internal documentation of class ForkJoinPool for a
* general implementation overview. ForkJoinTasks are mainly
* responsible for maintaining their "status" field amidst relays
- * to methods in ForkJoinWorkerThread and ForkJoinPool. The
- * methods of this class are more-or-less layered into (1) basic
- * status maintenance (2) execution and awaiting completion (3)
- * user-level methods that additionally report results. This is
- * sometimes hard to see because this file orders exported methods
- * in a way that flows well in javadocs.
+ * to methods in ForkJoinWorkerThread and ForkJoinPool.
+ *
+ * The methods of this class are more-or-less layered into
+ * (1) basic status maintenance
+ * (2) execution and awaiting completion
+ * (3) user-level methods that additionally report results.
+ * This is sometimes hard to see because this file orders exported
+ * methods in a way that flows well in javadocs.
*/
/*
* The status field holds run control status bits packed into a
* single int to minimize footprint and to ensure atomicity (via
* CAS). Status is initially zero, and takes on nonnegative
- * values until completed, upon which status holds value
- * NORMAL, CANCELLED, or EXCEPTIONAL. Tasks undergoing blocking
- * waits by other threads have the SIGNAL bit set. Completion of
- * a stolen task with SIGNAL set awakens any waiters via
- * notifyAll. Even though suboptimal for some purposes, we use
- * basic builtin wait/notify to take advantage of "monitor
- * inflation" in JVMs that we would otherwise need to emulate to
- * avoid adding further per-task bookkeeping overhead. We want
- * these monitors to be "fat", i.e., not use biasing or thin-lock
- * techniques, so use some odd coding idioms that tend to avoid
- * them.
+ * values until completed, upon which status (anded with
+ * DONE_MASK) holds value NORMAL, CANCELLED, or EXCEPTIONAL. Tasks
+ * undergoing blocking waits by other threads have the SIGNAL bit
+ * set. Completion of a stolen task with SIGNAL set awakens any
+ * waiters via notifyAll. Even though suboptimal for some
+ * purposes, we use basic builtin wait/notify to take advantage of
+ * "monitor inflation" in JVMs that we would otherwise need to
+ * emulate to avoid adding further per-task bookkeeping overhead.
+ * We want these monitors to be "fat", i.e., not use biasing or
+ * thin-lock techniques, so use some odd coding idioms that tend
+ * to avoid them, mainly by arranging that every synchronized
+ * block performs a wait, notifyAll or both.
+ *
+ * These control bits occupy only (some of) the upper half (16
+ * bits) of status field. The lower bits are used for user-defined
+ * tags.
*/
/** The run status of this task */
volatile int status; // accessed directly by pool and workers
- private static final int NORMAL = -1;
- private static final int CANCELLED = -2;
- private static final int EXCEPTIONAL = -3;
- private static final int SIGNAL = 1;
+ static final int DONE_MASK = 0xf0000000; // mask out non-completion bits
+ static final int NORMAL = 0xf0000000; // must be negative
+ static final int CANCELLED = 0xc0000000; // must be < NORMAL
+ static final int EXCEPTIONAL = 0x80000000; // must be < CANCELLED
+ static final int SIGNAL = 0x00010000; // must be >= 1 << 16
+ static final int SMASK = 0x0000ffff; // short bits for tags
/**
- * Marks completion and wakes up threads waiting to join this task,
- * also clearing signal request bits.
+ * Marks completion and wakes up threads waiting to join this
+ * task.
*
* @param completion one of NORMAL, CANCELLED, EXCEPTIONAL
* @return completion status on exit
@@ -237,8 +267,8 @@
for (int s;;) {
if ((s = status) < 0)
return s;
- if (UNSAFE.compareAndSwapInt(this, statusOffset, s, completion)) {
- if (s != 0)
+ if (U.compareAndSwapInt(this, STATUS, s, s | completion)) {
+ if ((s >>> 16) != 0)
synchronized (this) { notifyAll(); }
return completion;
}
@@ -246,27 +276,36 @@
}
/**
- * Tries to block a worker thread until completed or timed out.
- * Uses Object.wait time argument conventions.
- * May fail on contention or interrupt.
+ * Primary execution method for stolen tasks. Unless done, calls
+ * exec and records status if completed, but doesn't wait for
+ * completion otherwise.
*
- * @param millis if > 0, wait time.
+ * @return status on exit from this method
*/
- final void tryAwaitDone(long millis) {
- int s;
- try {
- if (((s = status) > 0 ||
- (s == 0 &&
- UNSAFE.compareAndSwapInt(this, statusOffset, 0, SIGNAL))) &&
- status > 0) {
- synchronized (this) {
- if (status > 0)
- wait(millis);
- }
+ final int doExec() {
+ int s; boolean completed;
+ if ((s = status) >= 0) {
+ try {
+ completed = exec();
+ } catch (Throwable rex) {
+ return setExceptionalCompletion(rex);
}
- } catch (InterruptedException ie) {
- // caller must check termination
+ if (completed)
+ s = setCompletion(NORMAL);
}
+ return s;
+ }
+
+ /**
+ * Tries to set SIGNAL status unless already completed. Used by
+ * ForkJoinPool. Other variants are directly incorporated into
+ * externalAwaitDone etc.
+ *
+ * @return true if successful
+ */
+ final boolean trySetSignal() {
+ int s = status;
+ return s >= 0 && U.compareAndSwapInt(this, STATUS, s, s | SIGNAL);
}
/**
@@ -275,113 +314,78 @@
*/
private int externalAwaitDone() {
int s;
- if ((s = status) >= 0) {
- boolean interrupted = false;
- synchronized (this) {
- while ((s = status) >= 0) {
- if (s == 0)
- UNSAFE.compareAndSwapInt(this, statusOffset,
- 0, SIGNAL);
- else {
+ ForkJoinPool.externalHelpJoin(this);
+ boolean interrupted = false;
+ while ((s = status) >= 0) {
+ if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0) {
try {
wait();
} catch (InterruptedException ie) {
interrupted = true;
}
}
+ else
+ notifyAll();
}
}
- if (interrupted)
- Thread.currentThread().interrupt();
}
+ if (interrupted)
+ Thread.currentThread().interrupt();
return s;
}
/**
- * Blocks a non-worker-thread until completion or interruption or timeout.
+ * Blocks a non-worker-thread until completion or interruption.
*/
- private int externalInterruptibleAwaitDone(long millis)
- throws InterruptedException {
+ private int externalInterruptibleAwaitDone() throws InterruptedException {
int s;
if (Thread.interrupted())
throw new InterruptedException();
- if ((s = status) >= 0) {
- synchronized (this) {
- while ((s = status) >= 0) {
- if (s == 0)
- UNSAFE.compareAndSwapInt(this, statusOffset,
- 0, SIGNAL);
- else {
- wait(millis);
- if (millis > 0L)
- break;
- }
+ ForkJoinPool.externalHelpJoin(this);
+ while ((s = status) >= 0) {
+ if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0)
+ wait();
+ else
+ notifyAll();
}
}
}
return s;
}
- /**
- * Primary execution method for stolen tasks. Unless done, calls
- * exec and records status if completed, but doesn't wait for
- * completion otherwise.
- */
- final void doExec() {
- if (status >= 0) {
- boolean completed;
- try {
- completed = exec();
- } catch (Throwable rex) {
- setExceptionalCompletion(rex);
- return;
- }
- if (completed)
- setCompletion(NORMAL); // must be outside try block
- }
- }
/**
- * Primary mechanics for join, get, quietlyJoin.
+ * Implementation for join, get, quietlyJoin. Directly handles
+ * only cases of already-completed, external wait, and
+ * unfork+exec. Others are relayed to ForkJoinPool.awaitJoin.
+ *
* @return status upon completion
*/
private int doJoin() {
- Thread t; ForkJoinWorkerThread w; int s; boolean completed;
- if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
- if ((s = status) < 0)
- return s;
- if ((w = (ForkJoinWorkerThread)t).unpushTask(this)) {
- try {
- completed = exec();
- } catch (Throwable rex) {
- return setExceptionalCompletion(rex);
- }
- if (completed)
- return setCompletion(NORMAL);
- }
- return w.joinTask(this);
- }
- else
- return externalAwaitDone();
+ int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;
+ return (s = status) < 0 ? s :
+ ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+ (w = (wt = (ForkJoinWorkerThread)t).workQueue).
+ tryUnpush(this) && (s = doExec()) < 0 ? s :
+ wt.pool.awaitJoin(w, this) :
+ externalAwaitDone();
}
/**
- * Primary mechanics for invoke, quietlyInvoke.
+ * Implementation for invoke, quietlyInvoke.
+ *
* @return status upon completion
*/
private int doInvoke() {
- int s; boolean completed;
- if ((s = status) < 0)
- return s;
- try {
- completed = exec();
- } catch (Throwable rex) {
- return setExceptionalCompletion(rex);
- }
- if (completed)
- return setCompletion(NORMAL);
- else
- return doJoin();
+ int s; Thread t; ForkJoinWorkerThread wt;
+ return (s = doExec()) < 0 ? s :
+ ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+ (wt = (ForkJoinWorkerThread)t).pool.awaitJoin(wt.workQueue, this) :
+ externalAwaitDone();
}
// Exception table support
@@ -416,7 +420,7 @@
* any ForkJoinPool will call helpExpungeStaleExceptions when its
* pool becomes isQuiescent.
*/
- static final class ExceptionNode extends WeakReference<ForkJoinTask<?>>{
+ static final class ExceptionNode extends WeakReference<ForkJoinTask<?>> {
final Throwable ex;
ExceptionNode next;
final long thrower; // use id not ref to avoid weak cycles
@@ -429,30 +433,67 @@
}
/**
- * Records exception and sets exceptional completion.
+ * Records exception and sets status.
+ *
+ * @return status on exit
+ */
+ final int recordExceptionalCompletion(Throwable ex) {
+ int s;
+ if ((s = status) >= 0) {
+ int h = System.identityHashCode(this);
+ final ReentrantLock lock = exceptionTableLock;
+ lock.lock();
+ try {
+ expungeStaleExceptions();
+ ExceptionNode[] t = exceptionTable;
+ int i = h & (t.length - 1);
+ for (ExceptionNode e = t[i]; ; e = e.next) {
+ if (e == null) {
+ t[i] = new ExceptionNode(this, ex, t[i]);
+ break;
+ }
+ if (e.get() == this) // already present
+ break;
+ }
+ } finally {
+ lock.unlock();
+ }
+ s = setCompletion(EXCEPTIONAL);
+ }
+ return s;
+ }
+
+ /**
+ * Records exception and possibly propagates
*
* @return status on exit
*/
private int setExceptionalCompletion(Throwable ex) {
- int h = System.identityHashCode(this);
- final ReentrantLock lock = exceptionTableLock;
- lock.lock();
- try {
- expungeStaleExceptions();
- ExceptionNode[] t = exceptionTable;
- int i = h & (t.length - 1);
- for (ExceptionNode e = t[i]; ; e = e.next) {
- if (e == null) {
- t[i] = new ExceptionNode(this, ex, t[i]);
- break;
- }
- if (e.get() == this) // already present
- break;
+ int s = recordExceptionalCompletion(ex);
+ if ((s & DONE_MASK) == EXCEPTIONAL)
+ internalPropagateException(ex);
+ return s;
+ }
+
+ /**
+ * Hook for exception propagation support for tasks with completers.
+ */
+ void internalPropagateException(Throwable ex) {
+ }
+
+ /**
+ * Cancels, ignoring any exceptions thrown by cancel. Used during
+ * worker and pool shutdown. Cancel is spec'ed not to throw any
+ * exceptions, but if it does anyway, we have no recourse during
+ * shutdown, so guard against this case.
+ */
+ static final void cancelIgnoringExceptions(ForkJoinTask<?> t) {
+ if (t != null && t.status >= 0) {
+ try {
+ t.cancel(false);
+ } catch (Throwable ignore) {
}
- } finally {
- lock.unlock();
}
- return setCompletion(EXCEPTIONAL);
}
/**
@@ -501,7 +542,7 @@
* @return the exception, or null if none
*/
private Throwable getThrowableException() {
- if (status != EXCEPTIONAL)
+ if ((status & DONE_MASK) != EXCEPTIONAL)
return null;
int h = System.identityHashCode(this);
ExceptionNode e;
@@ -519,7 +560,7 @@
Throwable ex;
if (e == null || (ex = e.ex) == null)
return null;
- if (e.thrower != Thread.currentThread().getId()) {
+ if (false && e.thrower != Thread.currentThread().getId()) {
Class<? extends Throwable> ec = ex.getClass();
try {
Constructor<?> noArgCtor = null;
@@ -586,41 +627,61 @@
}
/**
- * Report the result of invoke or join; called only upon
- * non-normal return of internal versions.
+ * A version of "sneaky throw" to relay exceptions
*/
- private V reportResult() {
- int s; Throwable ex;
- if ((s = status) == CANCELLED)
+ static void rethrow(final Throwable ex) {
+ if (ex != null) {
+ if (ex instanceof Error)
+ throw (Error)ex;
+ if (ex instanceof RuntimeException)
+ throw (RuntimeException)ex;
+ throw uncheckedThrowable(ex, RuntimeException.class);
+ }
+ }
+
+ /**
+ * The sneaky part of sneaky throw, relying on generics
+ * limitations to evade compiler complaints about rethrowing
+ * unchecked exceptions
+ */
+ @SuppressWarnings("unchecked") static <T extends Throwable>
+ T uncheckedThrowable(final Throwable t, final Class<T> c) {
+ return (T)t; // rely on vacuous cast
+ }
+
+ /**
+ * Throws exception, if any, associated with the given status.
+ */
+ private void reportException(int s) {
+ if (s == CANCELLED)
throw new CancellationException();
- if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
- UNSAFE.throwException(ex);
- return getRawResult();
+ if (s == EXCEPTIONAL)
+ rethrow(getThrowableException());
}
// public methods
/**
- * Arranges to asynchronously execute this task. While it is not
- * necessarily enforced, it is a usage error to fork a task more
- * than once unless it has completed and been reinitialized.
- * Subsequent modifications to the state of this task or any data
- * it operates on are not necessarily consistently observable by
- * any thread other than the one executing it unless preceded by a
- * call to {@link #join} or related methods, or a call to {@link
- * #isDone} returning {@code true}.
- *
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
+ * Arranges to asynchronously execute this task in the pool the
+ * current task is running in, if applicable, or using the {@link
+ * ForkJoinPool#commonPool()} if not {@link #inForkJoinPool}. While
+ * it is not necessarily enforced, it is a usage error to fork a
+ * task more than once unless it has completed and been
+ * reinitialized. Subsequent modifications to the state of this
+ * task or any data it operates on are not necessarily
+ * consistently observable by any thread other than the one
+ * executing it unless preceded by a call to {@link #join} or
+ * related methods, or a call to {@link #isDone} returning {@code
+ * true}.
*
* @return {@code this}, to simplify usage
*/
public final ForkJoinTask<V> fork() {
- ((ForkJoinWorkerThread) Thread.currentThread())
- .pushTask(this);
+ Thread t;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+ ((ForkJoinWorkerThread)t).workQueue.push(this);
+ else
+ ForkJoinPool.commonPool.externalPush(this);
return this;
}
@@ -636,10 +697,10 @@
* @return the computed result
*/
public final V join() {
- if (doJoin() != NORMAL)
- return reportResult();
- else
- return getRawResult();
+ int s;
+ if ((s = doJoin() & DONE_MASK) != NORMAL)
+ reportException(s);
+ return getRawResult();
}
/**
@@ -651,10 +712,10 @@
* @return the computed result
*/
public final V invoke() {
- if (doInvoke() != NORMAL)
- return reportResult();
- else
- return getRawResult();
+ int s;
+ if ((s = doInvoke() & DONE_MASK) != NORMAL)
+ reportException(s);
+ return getRawResult();
}
/**
@@ -670,20 +731,17 @@
* cancelled, completed normally or exceptionally, or left
* unprocessed.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @param t1 the first task
* @param t2 the second task
* @throws NullPointerException if any task is null
*/
public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
+ int s1, s2;
t2.fork();
- t1.invoke();
- t2.join();
+ if ((s1 = t1.doInvoke() & DONE_MASK) != NORMAL)
+ t1.reportException(s1);
+ if ((s2 = t2.doJoin() & DONE_MASK) != NORMAL)
+ t2.reportException(s2);
}
/**
@@ -698,12 +756,6 @@
* related methods to check if they have been cancelled, completed
* normally or exceptionally, or left unprocessed.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @param tasks the tasks
* @throws NullPointerException if any task is null
*/
@@ -726,12 +778,12 @@
if (t != null) {
if (ex != null)
t.cancel(false);
- else if (t.doJoin() < NORMAL && ex == null)
+ else if (t.doJoin() < NORMAL)
ex = t.getException();
}
}
if (ex != null)
- UNSAFE.throwException(ex);
+ rethrow(ex);
}
/**
@@ -747,12 +799,6 @@
* cancelled, completed normally or exceptionally, or left
* unprocessed.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @param tasks the collection of tasks
* @return the tasks argument, to simplify usage
* @throws NullPointerException if tasks or any element are null
@@ -783,12 +829,12 @@
if (t != null) {
if (ex != null)
t.cancel(false);
- else if (t.doJoin() < NORMAL && ex == null)
+ else if (t.doJoin() < NORMAL)
ex = t.getException();
}
}
if (ex != null)
- UNSAFE.throwException(ex);
+ rethrow(ex);
return tasks;
}
@@ -820,20 +866,7 @@
* @return {@code true} if this task is now cancelled
*/
public boolean cancel(boolean mayInterruptIfRunning) {
- return setCompletion(CANCELLED) == CANCELLED;
- }
-
- /**
- * Cancels, ignoring any exceptions thrown by cancel. Used during
- * worker and pool shutdown. Cancel is spec'ed not to throw any
- * exceptions, but if it does anyway, we have no recourse during
- * shutdown, so guard against this case.
- */
- final void cancelIgnoringExceptions() {
- try {
- cancel(false);
- } catch (Throwable ignore) {
- }
+ return (setCompletion(CANCELLED) & DONE_MASK) == CANCELLED;
}
public final boolean isDone() {
@@ -841,7 +874,7 @@
}
public final boolean isCancelled() {
- return status == CANCELLED;
+ return (status & DONE_MASK) == CANCELLED;
}
/**
@@ -861,7 +894,7 @@
* exception and was not cancelled
*/
public final boolean isCompletedNormally() {
- return status == NORMAL;
+ return (status & DONE_MASK) == NORMAL;
}
/**
@@ -872,7 +905,7 @@
* @return the exception, or {@code null} if none
*/
public final Throwable getException() {
- int s = status;
+ int s = status & DONE_MASK;
return ((s >= NORMAL) ? null :
(s == CANCELLED) ? new CancellationException() :
getThrowableException());
@@ -922,6 +955,18 @@
}
/**
+ * Completes this task normally without setting a value. The most
+ * recent value established by {@link #setRawResult} (or {@code
+ * null} by default) will be returned as the result of subsequent
+ * invocations of {@code join} and related operations.
+ *
+ * @since 1.8
+ */
+ public final void quietlyComplete() {
+ setCompletion(NORMAL);
+ }
+
+ /**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
@@ -934,9 +979,9 @@
*/
public final V get() throws InterruptedException, ExecutionException {
int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ?
- doJoin() : externalInterruptibleAwaitDone(0L);
+ doJoin() : externalInterruptibleAwaitDone();
Throwable ex;
- if (s == CANCELLED)
+ if ((s &= DONE_MASK) == CANCELLED)
throw new CancellationException();
if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
throw new ExecutionException(ex);
@@ -959,32 +1004,62 @@
*/
public final V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
- Thread t = Thread.currentThread();
- if (t instanceof ForkJoinWorkerThread) {
- ForkJoinWorkerThread w = (ForkJoinWorkerThread) t;
- long nanos = unit.toNanos(timeout);
- if (status >= 0) {
- boolean completed = false;
- if (w.unpushTask(this)) {
- try {
- completed = exec();
- } catch (Throwable rex) {
- setExceptionalCompletion(rex);
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ // Messy in part because we measure in nanosecs, but wait in millisecs
+ int s; long ns, ms;
+ if ((s = status) >= 0 && (ns = unit.toNanos(timeout)) > 0L) {
+ long deadline = System.nanoTime() + ns;
+ ForkJoinPool p = null;
+ ForkJoinPool.WorkQueue w = null;
+ Thread t = Thread.currentThread();
+ if (t instanceof ForkJoinWorkerThread) {
+ ForkJoinWorkerThread wt = (ForkJoinWorkerThread)t;
+ p = wt.pool;
+ w = wt.workQueue;
+ p.helpJoinOnce(w, this); // no retries on failure
+ }
+ else
+ ForkJoinPool.externalHelpJoin(this);
+ boolean canBlock = false;
+ boolean interrupted = false;
+ try {
+ while ((s = status) >= 0) {
+ if (w != null && w.qlock < 0)
+ cancelIgnoringExceptions(this);
+ else if (!canBlock) {
+ if (p == null || p.tryCompensate())
+ canBlock = true;
+ }
+ else {
+ if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
+ U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0) {
+ try {
+ wait(ms);
+ } catch (InterruptedException ie) {
+ if (p == null)
+ interrupted = true;
+ }
+ }
+ else
+ notifyAll();
+ }
+ }
+ if ((s = status) < 0 || interrupted ||
+ (ns = deadline - System.nanoTime()) <= 0L)
+ break;
}
}
- if (completed)
- setCompletion(NORMAL);
- else if (status >= 0 && nanos > 0)
- w.pool.timedAwaitJoin(this, nanos);
+ } finally {
+ if (p != null && canBlock)
+ p.incrementActiveCount();
}
+ if (interrupted)
+ throw new InterruptedException();
}
- else {
- long millis = unit.toMillis(timeout);
- if (millis > 0)
- externalInterruptibleAwaitDone(millis);
- }
- int s = status;
- if (s != NORMAL) {
+ if ((s &= DONE_MASK) != NORMAL) {
Throwable ex;
if (s == CANCELLED)
throw new CancellationException();
@@ -1021,16 +1096,15 @@
* be of use in designs in which many tasks are forked, but none
* are explicitly joined, instead executing them until all are
* processed.
- *
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
*/
public static void helpQuiesce() {
- ((ForkJoinWorkerThread) Thread.currentThread())
- .helpQuiescePool();
+ Thread t;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
+ ForkJoinWorkerThread wt = (ForkJoinWorkerThread)t;
+ wt.pool.helpQuiescePool(wt.workQueue);
+ }
+ else
+ ForkJoinPool.externalHelpQuiescePool();
}
/**
@@ -1050,7 +1124,7 @@
* setRawResult(null)}.
*/
public void reinitialize() {
- if (status == EXCEPTIONAL)
+ if ((status & DONE_MASK) == EXCEPTIONAL)
clearExceptionalCompletion();
else
status = 0;
@@ -1083,23 +1157,19 @@
/**
* Tries to unschedule this task for execution. This method will
- * typically succeed if this task is the most recently forked task
- * by the current thread, and has not commenced executing in
- * another thread. This method may be useful when arranging
- * alternative local processing of tasks that could have been, but
- * were not, stolen.
- *
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
+ * typically (but is not guaranteed to) succeed if this task is
+ * the most recently forked task by the current thread, and has
+ * not commenced executing in another thread. This method may be
+ * useful when arranging alternative local processing of tasks
+ * that could have been, but were not, stolen.
*
* @return {@code true} if unforked
*/
public boolean tryUnfork() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .unpushTask(this);
+ Thread t;
+ return (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+ ((ForkJoinWorkerThread)t).workQueue.tryUnpush(this) :
+ ForkJoinPool.tryExternalUnpush(this));
}
/**
@@ -1108,40 +1178,32 @@
* value may be useful for heuristic decisions about whether to
* fork other tasks.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @return the number of tasks
*/
public static int getQueuedTaskCount() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .getQueueSize();
+ Thread t; ForkJoinPool.WorkQueue q;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+ q = ((ForkJoinWorkerThread)t).workQueue;
+ else
+ q = ForkJoinPool.commonSubmitterQueue();
+ return (q == null) ? 0 : q.queueSize();
}
/**
* Returns an estimate of how many more locally queued tasks are
* held by the current worker thread than there are other worker
- * threads that might steal them. This value may be useful for
+ * threads that might steal them, or zero if this thread is not
+ * operating in a ForkJoinPool. This value may be useful for
* heuristic decisions about whether to fork other tasks. In many
* usages of ForkJoinTasks, at steady state, each worker should
* aim to maintain a small constant surplus (for example, 3) of
* tasks, and to process computations locally if this threshold is
* exceeded.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @return the surplus number of tasks, which may be negative
*/
public static int getSurplusQueuedTaskCount() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .getEstimatedSurplusTaskCount();
+ return ForkJoinPool.getSurplusQueuedTaskCount();
}
// Extension methods
@@ -1167,15 +1229,18 @@
protected abstract void setRawResult(V value);
/**
- * Immediately performs the base action of this task. This method
- * is designed to support extensions, and should not in general be
- * called otherwise. The return value controls whether this task
- * is considered to be done normally. It may return false in
+ * Immediately performs the base action of this task and returns
+ * true if, upon return from this method, this task is guaranteed
+ * to have completed normally. This method may return false
+ * otherwise, to indicate that this task is not necessarily
+ * complete (or is not known to be complete), for example in
* asynchronous actions that require explicit invocations of
- * {@link #complete} to become joinable. It may also throw an
- * (unchecked) exception to indicate abnormal exit.
+ * completion methods. This method may also throw an (unchecked)
+ * exception to indicate abnormal exit. This method is designed to
+ * support extensions, and should not in general be called
+ * otherwise.
*
- * @return {@code true} if completed normally
+ * @return {@code true} if this task is known to have completed normally
*/
protected abstract boolean exec();
@@ -1189,59 +1254,102 @@
* primarily to support extensions, and is unlikely to be useful
* otherwise.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @return the next task, or {@code null} if none are available
*/
protected static ForkJoinTask<?> peekNextLocalTask() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .peekTask();
+ Thread t; ForkJoinPool.WorkQueue q;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+ q = ((ForkJoinWorkerThread)t).workQueue;
+ else
+ q = ForkJoinPool.commonSubmitterQueue();
+ return (q == null) ? null : q.peek();
}
/**
* Unschedules and returns, without executing, the next task
- * queued by the current thread but not yet executed. This method
- * is designed primarily to support extensions, and is unlikely to
- * be useful otherwise.
- *
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
+ * queued by the current thread but not yet executed, if the
+ * current thread is operating in a ForkJoinPool. This method is
+ * designed primarily to support extensions, and is unlikely to be
+ * useful otherwise.
*
* @return the next task, or {@code null} if none are available
*/
protected static ForkJoinTask<?> pollNextLocalTask() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .pollLocalTask();
+ Thread t;
+ return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+ ((ForkJoinWorkerThread)t).workQueue.nextLocalTask() :
+ null;
}
/**
- * Unschedules and returns, without executing, the next task
+ * If the current thread is operating in a ForkJoinPool,
+ * unschedules and returns, without executing, the next task
* queued by the current thread but not yet executed, if one is
* available, or if not available, a task that was forked by some
* other thread, if available. Availability may be transient, so a
- * {@code null} result does not necessarily imply quiescence
- * of the pool this task is operating in. This method is designed
+ * {@code null} result does not necessarily imply quiescence of
+ * the pool this task is operating in. This method is designed
* primarily to support extensions, and is unlikely to be useful
* otherwise.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @return a task, or {@code null} if none are available
*/
protected static ForkJoinTask<?> pollTask() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .pollTask();
+ Thread t; ForkJoinWorkerThread wt;
+ return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+ (wt = (ForkJoinWorkerThread)t).pool.nextTaskFor(wt.workQueue) :
+ null;
+ }
+
+ // tag operations
+
+ /**
+ * Returns the tag for this task.
+ *
+ * @return the tag for this task
+ * @since 1.8
+ */
+ public final short getForkJoinTaskTag() {
+ return (short)status;
+ }
+
+ /**
+ * Atomically sets the tag value for this task.
+ *
+ * @param tag the tag value
+ * @return the previous value of the tag
+ * @since 1.8
+ */
+ public final short setForkJoinTaskTag(short tag) {
+ for (int s;;) {
+ if (U.compareAndSwapInt(this, STATUS, s = status,
+ (s & ~SMASK) | (tag & SMASK)))
+ return (short)s;
+ }
+ }
+
+ /**
+ * Atomically conditionally sets the tag value for this task.
+ * Among other applications, tags can be used as visit markers
+ * in tasks operating on graphs, as in methods that check: {@code
+ * if (task.compareAndSetForkJoinTaskTag((short)0, (short)1))}
+ * before processing, otherwise exiting because the node has
+ * already been visited.
+ *
+ * @param e the expected tag value
+ * @param tag the new tag value
+ * @return true if successful; i.e., the current value was
+ * equal to e and is now tag.
+ * @since 1.8
+ */
+ public final boolean compareAndSetForkJoinTaskTag(short e, short tag) {
+ for (int s;;) {
+ if ((short)(s = status) != e)
+ return false;
+ if (U.compareAndSwapInt(this, STATUS, s,
+ (s & ~SMASK) | (tag & SMASK)))
+ return true;
+ }
}
/**
@@ -1252,21 +1360,33 @@
static final class AdaptedRunnable<T> extends ForkJoinTask<T>
implements RunnableFuture<T> {
final Runnable runnable;
- final T resultOnCompletion;
T result;
AdaptedRunnable(Runnable runnable, T result) {
if (runnable == null) throw new NullPointerException();
this.runnable = runnable;
- this.resultOnCompletion = result;
+ this.result = result; // OK to set this even before completion
}
- public T getRawResult() { return result; }
- public void setRawResult(T v) { result = v; }
- public boolean exec() {
- runnable.run();
- result = resultOnCompletion;
- return true;
+ public final T getRawResult() { return result; }
+ public final void setRawResult(T v) { result = v; }
+ public final boolean exec() { runnable.run(); return true; }
+ public final void run() { invoke(); }
+ private static final long serialVersionUID = 5232453952276885070L;
+ }
+
+ /**
+ * Adaptor for Runnables without results
+ */
+ static final class AdaptedRunnableAction extends ForkJoinTask<Void>
+ implements RunnableFuture<Void> {
+ final Runnable runnable;
+ AdaptedRunnableAction(Runnable runnable) {
+ if (runnable == null) throw new NullPointerException();
+ this.runnable = runnable;
}
- public void run() { invoke(); }
+ public final Void getRawResult() { return null; }
+ public final void setRawResult(Void v) { }
+ public final boolean exec() { runnable.run(); return true; }
+ public final void run() { invoke(); }
private static final long serialVersionUID = 5232453952276885070L;
}
@@ -1281,9 +1401,9 @@
if (callable == null) throw new NullPointerException();
this.callable = callable;
}
- public T getRawResult() { return result; }
- public void setRawResult(T v) { result = v; }
- public boolean exec() {
+ public final T getRawResult() { return result; }
+ public final void setRawResult(T v) { result = v; }
+ public final boolean exec() {
try {
result = callable.call();
return true;
@@ -1295,7 +1415,7 @@
throw new RuntimeException(ex);
}
}
- public void run() { invoke(); }
+ public final void run() { invoke(); }
private static final long serialVersionUID = 2838392045355241008L;
}
@@ -1308,7 +1428,7 @@
* @return the task
*/
public static ForkJoinTask<?> adapt(Runnable runnable) {
- return new AdaptedRunnable<Void>(runnable, null);
+ return new AdaptedRunnableAction(runnable);
}
/**
@@ -1342,11 +1462,10 @@
private static final long serialVersionUID = -7721805057305804111L;
/**
- * Saves the state to a stream (that is, serializes it).
+ * Saves this task to a stream (that is, serializes it).
*
* @serialData the current run status and the exception thrown
* during execution, or {@code null} if none
- * @param s the stream
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
@@ -1355,9 +1474,7 @@
}
/**
- * Reconstitutes the instance from a stream (that is, deserializes it).
- *
- * @param s the stream
+ * Reconstitutes this task from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -1368,16 +1485,18 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long statusOffset;
+ private static final sun.misc.Unsafe U;
+ private static final long STATUS;
+
static {
exceptionTableLock = new ReentrantLock();
exceptionTableRefQueue = new ReferenceQueue<Object>();
exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY];
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- statusOffset = UNSAFE.objectFieldOffset
- (ForkJoinTask.class.getDeclaredField("status"));
+ U = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ForkJoinTask.class;
+ STATUS = U.objectFieldOffset
+ (k.getDeclaredField("status"));
} catch (Exception e) {
throw new Error(e);
}
--- a/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Fri Dec 28 18:36:41 2012 -0800
@@ -35,9 +35,6 @@
package java.util.concurrent;
-import java.util.Collection;
-import java.util.concurrent.RejectedExecutionException;
-
/**
* A thread managed by a {@link ForkJoinPool}, which executes
* {@link ForkJoinTask}s.
@@ -54,238 +51,20 @@
*/
public class ForkJoinWorkerThread extends Thread {
/*
- * Overview:
- *
* ForkJoinWorkerThreads are managed by ForkJoinPools and perform
- * ForkJoinTasks. This class includes bookkeeping in support of
- * worker activation, suspension, and lifecycle control described
- * in more detail in the internal documentation of class
- * ForkJoinPool. And as described further below, this class also
- * includes special-cased support for some ForkJoinTask
- * methods. But the main mechanics involve work-stealing:
- *
- * Work-stealing queues are special forms of Deques that support
- * only three of the four possible end-operations -- push, pop,
- * and deq (aka steal), under the further constraints that push
- * and pop are called only from the owning thread, while deq may
- * be called from other threads. (If you are unfamiliar with
- * them, you probably want to read Herlihy and Shavit's book "The
- * Art of Multiprocessor programming", chapter 16 describing these
- * in more detail before proceeding.) The main work-stealing
- * queue design is roughly similar to those in the papers "Dynamic
- * Circular Work-Stealing Deque" by Chase and Lev, SPAA 2005
- * (http://research.sun.com/scalable/pubs/index.html) and
- * "Idempotent work stealing" by Michael, Saraswat, and Vechev,
- * PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186).
- * The main differences ultimately stem from gc requirements that
- * we null out taken slots as soon as we can, to maintain as small
- * a footprint as possible even in programs generating huge
- * numbers of tasks. To accomplish this, we shift the CAS
- * arbitrating pop vs deq (steal) from being on the indices
- * ("queueBase" and "queueTop") to the slots themselves (mainly
- * via method "casSlotNull()"). So, both a successful pop and deq
- * mainly entail a CAS of a slot from non-null to null. Because
- * we rely on CASes of references, we do not need tag bits on
- * queueBase or queueTop. They are simple ints as used in any
- * circular array-based queue (see for example ArrayDeque).
- * Updates to the indices must still be ordered in a way that
- * guarantees that queueTop == queueBase means the queue is empty,
- * but otherwise may err on the side of possibly making the queue
- * appear nonempty when a push, pop, or deq have not fully
- * committed. Note that this means that the deq operation,
- * considered individually, is not wait-free. One thief cannot
- * successfully continue until another in-progress one (or, if
- * previously empty, a push) completes. However, in the
- * aggregate, we ensure at least probabilistic non-blockingness.
- * If an attempted steal fails, a thief always chooses a different
- * random victim target to try next. So, in order for one thief to
- * progress, it suffices for any in-progress deq or new push on
- * any empty queue to complete.
+ * ForkJoinTasks. For explanation, see the internal documentation
+ * of class ForkJoinPool.
*
- * This approach also enables support for "async mode" where local
- * task processing is in FIFO, not LIFO order; simply by using a
- * version of deq rather than pop when locallyFifo is true (as set
- * by the ForkJoinPool). This allows use in message-passing
- * frameworks in which tasks are never joined. However neither
- * mode considers affinities, loads, cache localities, etc, so
- * rarely provide the best possible performance on a given
- * machine, but portably provide good throughput by averaging over
- * these factors. (Further, even if we did try to use such
- * information, we do not usually have a basis for exploiting
- * it. For example, some sets of tasks profit from cache
- * affinities, but others are harmed by cache pollution effects.)
- *
- * When a worker would otherwise be blocked waiting to join a
- * task, it first tries a form of linear helping: Each worker
- * records (in field currentSteal) the most recent task it stole
- * from some other worker. Plus, it records (in field currentJoin)
- * the task it is currently actively joining. Method joinTask uses
- * these markers to try to find a worker to help (i.e., steal back
- * a task from and execute it) that could hasten completion of the
- * actively joined task. In essence, the joiner executes a task
- * that would be on its own local deque had the to-be-joined task
- * not been stolen. This may be seen as a conservative variant of
- * the approach in Wagner & Calder "Leapfrogging: a portable
- * technique for implementing efficient futures" SIGPLAN Notices,
- * 1993 (http://portal.acm.org/citation.cfm?id=155354). It differs
- * in that: (1) We only maintain dependency links across workers
- * upon steals, rather than use per-task bookkeeping. This may
- * require a linear scan of workers array to locate stealers, but
- * usually doesn't because stealers leave hints (that may become
- * stale/wrong) of where to locate them. This isolates cost to
- * when it is needed, rather than adding to per-task overhead.
- * (2) It is "shallow", ignoring nesting and potentially cyclic
- * mutual steals. (3) It is intentionally racy: field currentJoin
- * is updated only while actively joining, which means that we
- * miss links in the chain during long-lived tasks, GC stalls etc
- * (which is OK since blocking in such cases is usually a good
- * idea). (4) We bound the number of attempts to find work (see
- * MAX_HELP) and fall back to suspending the worker and if
- * necessary replacing it with another.
- *
- * Efficient implementation of these algorithms currently relies
- * on an uncomfortable amount of "Unsafe" mechanics. To maintain
- * correct orderings, reads and writes of variable queueBase
- * require volatile ordering. Variable queueTop need not be
- * volatile because non-local reads always follow those of
- * queueBase. Similarly, because they are protected by volatile
- * queueBase reads, reads of the queue array and its slots by
- * other threads do not need volatile load semantics, but writes
- * (in push) require store order and CASes (in pop and deq)
- * require (volatile) CAS semantics. (Michael, Saraswat, and
- * Vechev's algorithm has similar properties, but without support
- * for nulling slots.) Since these combinations aren't supported
- * using ordinary volatiles, the only way to accomplish these
- * efficiently is to use direct Unsafe calls. (Using external
- * AtomicIntegers and AtomicReferenceArrays for the indices and
- * array is significantly slower because of memory locality and
- * indirection effects.)
- *
- * Further, performance on most platforms is very sensitive to
- * placement and sizing of the (resizable) queue array. Even
- * though these queues don't usually become all that big, the
- * initial size must be large enough to counteract cache
- * contention effects across multiple queues (especially in the
- * presence of GC cardmarking). Also, to improve thread-locality,
- * queues are initialized after starting.
+ * This class just maintains links to its pool and WorkQueue. The
+ * pool field is set immediately upon construction, but the
+ * workQueue field is not set until a call to registerWorker
+ * completes. This leads to a visibility race, that is tolerated
+ * by requiring that the workQueue field is only accessed by the
+ * owning thread.
*/
- /**
- * Mask for pool indices encoded as shorts
- */
- private static final int SMASK = 0xffff;
-
- /**
- * Capacity of work-stealing queue array upon initialization.
- * Must be a power of two. Initial size must be at least 4, but is
- * padded to minimize cache effects.
- */
- private static final int INITIAL_QUEUE_CAPACITY = 1 << 13;
-
- /**
- * Maximum size for queue array. Must be a power of two
- * less than or equal to 1 << (31 - width of array entry) to
- * ensure lack of index wraparound, but is capped at a lower
- * value to help users trap runaway computations.
- */
- private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 24; // 16M
-
- /**
- * The work-stealing queue array. Size must be a power of two.
- * Initialized when started (as opposed to when constructed), to
- * improve memory locality.
- */
- ForkJoinTask<?>[] queue;
-
- /**
- * The pool this thread works in. Accessed directly by ForkJoinTask.
- */
- final ForkJoinPool pool;
-
- /**
- * Index (mod queue.length) of next queue slot to push to or pop
- * from. It is written only by owner thread, and accessed by other
- * threads only after reading (volatile) queueBase. Both queueTop
- * and queueBase are allowed to wrap around on overflow, but
- * (queueTop - queueBase) still estimates size.
- */
- int queueTop;
-
- /**
- * Index (mod queue.length) of least valid queue slot, which is
- * always the next position to steal from if nonempty.
- */
- volatile int queueBase;
-
- /**
- * The index of most recent stealer, used as a hint to avoid
- * traversal in method helpJoinTask. This is only a hint because a
- * worker might have had multiple steals and this only holds one
- * of them (usually the most current). Declared non-volatile,
- * relying on other prevailing sync to keep reasonably current.
- */
- int stealHint;
-
- /**
- * Index of this worker in pool array. Set once by pool before
- * running, and accessed directly by pool to locate this worker in
- * its workers array.
- */
- final int poolIndex;
-
- /**
- * Encoded record for pool task waits. Usages are always
- * surrounded by volatile reads/writes
- */
- int nextWait;
-
- /**
- * Complement of poolIndex, offset by count of entries of task
- * waits. Accessed by ForkJoinPool to manage event waiters.
- */
- volatile int eventCount;
-
- /**
- * Seed for random number generator for choosing steal victims.
- * Uses Marsaglia xorshift. Must be initialized as nonzero.
- */
- int seed;
-
- /**
- * Number of steals. Directly accessed (and reset) by pool when
- * idle.
- */
- int stealCount;
-
- /**
- * True if this worker should or did terminate
- */
- volatile boolean terminate;
-
- /**
- * Set to true before LockSupport.park; false on return
- */
- volatile boolean parked;
-
- /**
- * True if use local fifo, not default lifo, for local polling.
- * Shadows value from ForkJoinPool.
- */
- final boolean locallyFifo;
-
- /**
- * The task most recently stolen from another worker (or
- * submission queue). All uses are surrounded by enough volatile
- * reads/writes to maintain as non-volatile.
- */
- ForkJoinTask<?> currentSteal;
-
- /**
- * The task currently being joined, set only when actively trying
- * to help other stealers in helpJoinTask. All uses are surrounded
- * by enough volatile reads/writes to maintain as non-volatile.
- */
- ForkJoinTask<?> currentJoin;
+ final ForkJoinPool pool; // the pool this thread works in
+ final ForkJoinPool.WorkQueue workQueue; // work-stealing mechanics
/**
* Creates a ForkJoinWorkerThread operating in the given pool.
@@ -294,20 +73,12 @@
* @throws NullPointerException if pool is null
*/
protected ForkJoinWorkerThread(ForkJoinPool pool) {
- super(pool.nextWorkerName());
+ // Use a placeholder until a useful name can be set in registerWorker
+ super("aForkJoinWorkerThread");
this.pool = pool;
- int k = pool.registerWorker(this);
- poolIndex = k;
- eventCount = ~k & SMASK; // clear wait count
- locallyFifo = pool.locallyFifo;
- Thread.UncaughtExceptionHandler ueh = pool.ueh;
- if (ueh != null)
- setUncaughtExceptionHandler(ueh);
- setDaemon(true);
+ this.workQueue = pool.registerWorker(this);
}
- // Public methods
-
/**
* Returns the pool hosting this thread.
*
@@ -327,28 +98,9 @@
* @return the index number
*/
public int getPoolIndex() {
- return poolIndex;
+ return workQueue.poolIndex;
}
- // Randomization
-
- /**
- * Computes next value for random victim probes and backoffs.
- * Scans don't require a very high quality generator, but also not
- * a crummy one. Marsaglia xor-shift is cheap and works well
- * enough. Note: This is manually inlined in FJP.scan() to avoid
- * writes inside busy loops.
- */
- private int nextSeed() {
- int r = seed;
- r ^= r << 13;
- r ^= r >>> 17;
- r ^= r << 5;
- return seed = r;
- }
-
- // Run State management
-
/**
* Initializes internal state after construction but before
* processing any tasks. If you override this method, you must
@@ -359,9 +111,6 @@
* processing tasks.
*/
protected void onStart() {
- queue = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY];
- int r = ForkJoinPool.workerSeedGenerator.nextInt();
- seed = (r == 0) ? 1 : r; // must be nonzero
}
/**
@@ -373,17 +122,6 @@
* to an unrecoverable error, or {@code null} if completed normally
*/
protected void onTermination(Throwable exception) {
- try {
- terminate = true;
- cancelTasks();
- pool.deregisterWorker(this, exception);
- } catch (Throwable ex) { // Shouldn't ever happen
- if (exception == null) // but if so, at least rethrown
- exception = ex;
- } finally {
- if (exception != null)
- UNSAFE.throwException(exception);
- }
}
/**
@@ -395,604 +133,18 @@
Throwable exception = null;
try {
onStart();
- pool.work(this);
+ pool.runWorker(workQueue);
} catch (Throwable ex) {
exception = ex;
} finally {
- onTermination(exception);
- }
- }
-
- /*
- * Intrinsics-based atomic writes for queue slots. These are
- * basically the same as methods in AtomicReferenceArray, but
- * specialized for (1) ForkJoinTask elements (2) requirement that
- * nullness and bounds checks have already been performed by
- * callers and (3) effective offsets are known not to overflow
- * from int to long (because of MAXIMUM_QUEUE_CAPACITY). We don't
- * need corresponding version for reads: plain array reads are OK
- * because they are protected by other volatile reads and are
- * confirmed by CASes.
- *
- * Most uses don't actually call these methods, but instead
- * contain inlined forms that enable more predictable
- * optimization. We don't define the version of write used in
- * pushTask at all, but instead inline there a store-fenced array
- * slot write.
- *
- * Also in most methods, as a performance (not correctness) issue,
- * we'd like to encourage compilers not to arbitrarily postpone
- * setting queueTop after writing slot. Currently there is no
- * intrinsic for arranging this, but using Unsafe putOrderedInt
- * may be a preferable strategy on some compilers even though its
- * main effect is a pre-, not post- fence. To simplify possible
- * changes, the option is left in comments next to the associated
- * assignments.
- */
-
- /**
- * CASes slot i of array q from t to null. Caller must ensure q is
- * non-null and index is in range.
- */
- private static final boolean casSlotNull(ForkJoinTask<?>[] q, int i,
- ForkJoinTask<?> t) {
- return UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE, t, null);
- }
-
- /**
- * Performs a volatile write of the given task at given slot of
- * array q. Caller must ensure q is non-null and index is in
- * range. This method is used only during resets and backouts.
- */
- private static final void writeSlot(ForkJoinTask<?>[] q, int i,
- ForkJoinTask<?> t) {
- UNSAFE.putObjectVolatile(q, (i << ASHIFT) + ABASE, t);
- }
-
- // queue methods
-
- /**
- * Pushes a task. Call only from this thread.
- *
- * @param t the task. Caller must ensure non-null.
- */
- final void pushTask(ForkJoinTask<?> t) {
- ForkJoinTask<?>[] q; int s, m;
- if ((q = queue) != null) { // ignore if queue removed
- long u = (((s = queueTop) & (m = q.length - 1)) << ASHIFT) + ABASE;
- UNSAFE.putOrderedObject(q, u, t);
- queueTop = s + 1; // or use putOrderedInt
- if ((s -= queueBase) <= 2)
- pool.signalWork();
- else if (s == m)
- growQueue();
- }
- }
-
- /**
- * Creates or doubles queue array. Transfers elements by
- * emulating steals (deqs) from old array and placing, oldest
- * first, into new array.
- */
- private void growQueue() {
- ForkJoinTask<?>[] oldQ = queue;
- int size = oldQ != null ? oldQ.length << 1 : INITIAL_QUEUE_CAPACITY;
- if (size > MAXIMUM_QUEUE_CAPACITY)
- throw new RejectedExecutionException("Queue capacity exceeded");
- if (size < INITIAL_QUEUE_CAPACITY)
- size = INITIAL_QUEUE_CAPACITY;
- ForkJoinTask<?>[] q = queue = new ForkJoinTask<?>[size];
- int mask = size - 1;
- int top = queueTop;
- int oldMask;
- if (oldQ != null && (oldMask = oldQ.length - 1) >= 0) {
- for (int b = queueBase; b != top; ++b) {
- long u = ((b & oldMask) << ASHIFT) + ABASE;
- Object x = UNSAFE.getObjectVolatile(oldQ, u);
- if (x != null && UNSAFE.compareAndSwapObject(oldQ, u, x, null))
- UNSAFE.putObjectVolatile
- (q, ((b & mask) << ASHIFT) + ABASE, x);
+ try {
+ onTermination(exception);
+ } catch (Throwable ex) {
+ if (exception == null)
+ exception = ex;
+ } finally {
+ pool.deregisterWorker(this, exception);
}
}
}
-
- /**
- * Tries to take a task from the base of the queue, failing if
- * empty or contended. Note: Specializations of this code appear
- * in locallyDeqTask and elsewhere.
- *
- * @return a task, or null if none or contended
- */
- final ForkJoinTask<?> deqTask() {
- ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
- if (queueTop != (b = queueBase) &&
- (q = queue) != null && // must read q after b
- (i = (q.length - 1) & b) >= 0 &&
- (t = q[i]) != null && queueBase == b &&
- UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE, t, null)) {
- queueBase = b + 1;
- return t;
- }
- return null;
- }
-
- /**
- * Tries to take a task from the base of own queue. Called only
- * by this thread.
- *
- * @return a task, or null if none
- */
- final ForkJoinTask<?> locallyDeqTask() {
- ForkJoinTask<?> t; int m, b, i;
- ForkJoinTask<?>[] q = queue;
- if (q != null && (m = q.length - 1) >= 0) {
- while (queueTop != (b = queueBase)) {
- if ((t = q[i = m & b]) != null &&
- queueBase == b &&
- UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE,
- t, null)) {
- queueBase = b + 1;
- return t;
- }
- }
- }
- return null;
- }
-
- /**
- * Returns a popped task, or null if empty.
- * Called only by this thread.
- */
- private ForkJoinTask<?> popTask() {
- int m;
- ForkJoinTask<?>[] q = queue;
- if (q != null && (m = q.length - 1) >= 0) {
- for (int s; (s = queueTop) != queueBase;) {
- int i = m & --s;
- long u = (i << ASHIFT) + ABASE; // raw offset
- ForkJoinTask<?> t = q[i];
- if (t == null) // lost to stealer
- break;
- if (UNSAFE.compareAndSwapObject(q, u, t, null)) {
- queueTop = s; // or putOrderedInt
- return t;
- }
- }
- }
- return null;
- }
-
- /**
- * Specialized version of popTask to pop only if topmost element
- * is the given task. Called only by this thread.
- *
- * @param t the task. Caller must ensure non-null.
- */
- final boolean unpushTask(ForkJoinTask<?> t) {
- ForkJoinTask<?>[] q;
- int s;
- if ((q = queue) != null && (s = queueTop) != queueBase &&
- UNSAFE.compareAndSwapObject
- (q, (((q.length - 1) & --s) << ASHIFT) + ABASE, t, null)) {
- queueTop = s; // or putOrderedInt
- return true;
- }
- return false;
- }
-
- /**
- * Returns next task, or null if empty or contended.
- */
- final ForkJoinTask<?> peekTask() {
- int m;
- ForkJoinTask<?>[] q = queue;
- if (q == null || (m = q.length - 1) < 0)
- return null;
- int i = locallyFifo ? queueBase : (queueTop - 1);
- return q[i & m];
- }
-
- // Support methods for ForkJoinPool
-
- /**
- * Runs the given task, plus any local tasks until queue is empty
- */
- final void execTask(ForkJoinTask<?> t) {
- currentSteal = t;
- for (;;) {
- if (t != null)
- t.doExec();
- if (queueTop == queueBase)
- break;
- t = locallyFifo ? locallyDeqTask() : popTask();
- }
- ++stealCount;
- currentSteal = null;
- }
-
- /**
- * Removes and cancels all tasks in queue. Can be called from any
- * thread.
- */
- final void cancelTasks() {
- ForkJoinTask<?> cj = currentJoin; // try to cancel ongoing tasks
- if (cj != null && cj.status >= 0)
- cj.cancelIgnoringExceptions();
- ForkJoinTask<?> cs = currentSteal;
- if (cs != null && cs.status >= 0)
- cs.cancelIgnoringExceptions();
- while (queueBase != queueTop) {
- ForkJoinTask<?> t = deqTask();
- if (t != null)
- t.cancelIgnoringExceptions();
- }
- }
-
- /**
- * Drains tasks to given collection c.
- *
- * @return the number of tasks drained
- */
- final int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
- int n = 0;
- while (queueBase != queueTop) {
- ForkJoinTask<?> t = deqTask();
- if (t != null) {
- c.add(t);
- ++n;
- }
- }
- return n;
- }
-
- // Support methods for ForkJoinTask
-
- /**
- * Returns an estimate of the number of tasks in the queue.
- */
- final int getQueueSize() {
- return queueTop - queueBase;
- }
-
- /**
- * Gets and removes a local task.
- *
- * @return a task, if available
- */
- final ForkJoinTask<?> pollLocalTask() {
- return locallyFifo ? locallyDeqTask() : popTask();
- }
-
- /**
- * Gets and removes a local or stolen task.
- *
- * @return a task, if available
- */
- final ForkJoinTask<?> pollTask() {
- ForkJoinWorkerThread[] ws;
- ForkJoinTask<?> t = pollLocalTask();
- if (t != null || (ws = pool.workers) == null)
- return t;
- int n = ws.length; // cheap version of FJP.scan
- int steps = n << 1;
- int r = nextSeed();
- int i = 0;
- while (i < steps) {
- ForkJoinWorkerThread w = ws[(i++ + r) & (n - 1)];
- if (w != null && w.queueBase != w.queueTop && w.queue != null) {
- if ((t = w.deqTask()) != null)
- return t;
- i = 0;
- }
- }
- return null;
- }
-
- /**
- * The maximum stolen->joining link depth allowed in helpJoinTask,
- * as well as the maximum number of retries (allowing on average
- * one staleness retry per level) per attempt to instead try
- * compensation. Depths for legitimate chains are unbounded, but
- * we use a fixed constant to avoid (otherwise unchecked) cycles
- * and bound staleness of traversal parameters at the expense of
- * sometimes blocking when we could be helping.
- */
- private static final int MAX_HELP = 16;
-
- /**
- * Possibly runs some tasks and/or blocks, until joinMe is done.
- *
- * @param joinMe the task to join
- * @return completion status on exit
- */
- final int joinTask(ForkJoinTask<?> joinMe) {
- ForkJoinTask<?> prevJoin = currentJoin;
- currentJoin = joinMe;
- for (int s, retries = MAX_HELP;;) {
- if ((s = joinMe.status) < 0) {
- currentJoin = prevJoin;
- return s;
- }
- if (retries > 0) {
- if (queueTop != queueBase) {
- if (!localHelpJoinTask(joinMe))
- retries = 0; // cannot help
- }
- else if (retries == MAX_HELP >>> 1) {
- --retries; // check uncommon case
- if (tryDeqAndExec(joinMe) >= 0)
- Thread.yield(); // for politeness
- }
- else
- retries = helpJoinTask(joinMe) ? MAX_HELP : retries - 1;
- }
- else {
- retries = MAX_HELP; // restart if not done
- pool.tryAwaitJoin(joinMe);
- }
- }
- }
-
- /**
- * If present, pops and executes the given task, or any other
- * cancelled task
- *
- * @return false if any other non-cancelled task exists in local queue
- */
- private boolean localHelpJoinTask(ForkJoinTask<?> joinMe) {
- int s, i; ForkJoinTask<?>[] q; ForkJoinTask<?> t;
- if ((s = queueTop) != queueBase && (q = queue) != null &&
- (i = (q.length - 1) & --s) >= 0 &&
- (t = q[i]) != null) {
- if (t != joinMe && t.status >= 0)
- return false;
- if (UNSAFE.compareAndSwapObject
- (q, (i << ASHIFT) + ABASE, t, null)) {
- queueTop = s; // or putOrderedInt
- t.doExec();
- }
- }
- return true;
- }
-
- /**
- * Tries to locate and execute tasks for a stealer of the given
- * task, or in turn one of its stealers, Traces
- * currentSteal->currentJoin links looking for a thread working on
- * a descendant of the given task and with a non-empty queue to
- * steal back and execute tasks from. The implementation is very
- * branchy to cope with potential inconsistencies or loops
- * encountering chains that are stale, unknown, or of length
- * greater than MAX_HELP links. All of these cases are dealt with
- * by just retrying by caller.
- *
- * @param joinMe the task to join
- * @param canSteal true if local queue is empty
- * @return true if ran a task
- */
- private boolean helpJoinTask(ForkJoinTask<?> joinMe) {
- boolean helped = false;
- int m = pool.scanGuard & SMASK;
- ForkJoinWorkerThread[] ws = pool.workers;
- if (ws != null && ws.length > m && joinMe.status >= 0) {
- int levels = MAX_HELP; // remaining chain length
- ForkJoinTask<?> task = joinMe; // base of chain
- outer:for (ForkJoinWorkerThread thread = this;;) {
- // Try to find v, the stealer of task, by first using hint
- ForkJoinWorkerThread v = ws[thread.stealHint & m];
- if (v == null || v.currentSteal != task) {
- for (int j = 0; ;) { // search array
- if ((v = ws[j]) != null && v.currentSteal == task) {
- thread.stealHint = j;
- break; // save hint for next time
- }
- if (++j > m)
- break outer; // can't find stealer
- }
- }
- // Try to help v, using specialized form of deqTask
- for (;;) {
- ForkJoinTask<?>[] q; int b, i;
- if (joinMe.status < 0)
- break outer;
- if ((b = v.queueBase) == v.queueTop ||
- (q = v.queue) == null ||
- (i = (q.length-1) & b) < 0)
- break; // empty
- long u = (i << ASHIFT) + ABASE;
- ForkJoinTask<?> t = q[i];
- if (task.status < 0)
- break outer; // stale
- if (t != null && v.queueBase == b &&
- UNSAFE.compareAndSwapObject(q, u, t, null)) {
- v.queueBase = b + 1;
- v.stealHint = poolIndex;
- ForkJoinTask<?> ps = currentSteal;
- currentSteal = t;
- t.doExec();
- currentSteal = ps;
- helped = true;
- }
- }
- // Try to descend to find v's stealer
- ForkJoinTask<?> next = v.currentJoin;
- if (--levels > 0 && task.status >= 0 &&
- next != null && next != task) {
- task = next;
- thread = v;
- }
- else
- break; // max levels, stale, dead-end, or cyclic
- }
- }
- return helped;
- }
-
- /**
- * Performs an uncommon case for joinTask: If task t is at base of
- * some workers queue, steals and executes it.
- *
- * @param t the task
- * @return t's status
- */
- private int tryDeqAndExec(ForkJoinTask<?> t) {
- int m = pool.scanGuard & SMASK;
- ForkJoinWorkerThread[] ws = pool.workers;
- if (ws != null && ws.length > m && t.status >= 0) {
- for (int j = 0; j <= m; ++j) {
- ForkJoinTask<?>[] q; int b, i;
- ForkJoinWorkerThread v = ws[j];
- if (v != null &&
- (b = v.queueBase) != v.queueTop &&
- (q = v.queue) != null &&
- (i = (q.length - 1) & b) >= 0 &&
- q[i] == t) {
- long u = (i << ASHIFT) + ABASE;
- if (v.queueBase == b &&
- UNSAFE.compareAndSwapObject(q, u, t, null)) {
- v.queueBase = b + 1;
- v.stealHint = poolIndex;
- ForkJoinTask<?> ps = currentSteal;
- currentSteal = t;
- t.doExec();
- currentSteal = ps;
- }
- break;
- }
- }
- }
- return t.status;
- }
-
- /**
- * Implements ForkJoinTask.getSurplusQueuedTaskCount(). Returns
- * an estimate of the number of tasks, offset by a function of
- * number of idle workers.
- *
- * This method provides a cheap heuristic guide for task
- * partitioning when programmers, frameworks, tools, or languages
- * have little or no idea about task granularity. In essence by
- * offering this method, we ask users only about tradeoffs in
- * overhead vs expected throughput and its variance, rather than
- * how finely to partition tasks.
- *
- * In a steady state strict (tree-structured) computation, each
- * thread makes available for stealing enough tasks for other
- * threads to remain active. Inductively, if all threads play by
- * the same rules, each thread should make available only a
- * constant number of tasks.
- *
- * The minimum useful constant is just 1. But using a value of 1
- * would require immediate replenishment upon each steal to
- * maintain enough tasks, which is infeasible. Further,
- * partitionings/granularities of offered tasks should minimize
- * steal rates, which in general means that threads nearer the top
- * of computation tree should generate more than those nearer the
- * bottom. In perfect steady state, each thread is at
- * approximately the same level of computation tree. However,
- * producing extra tasks amortizes the uncertainty of progress and
- * diffusion assumptions.
- *
- * So, users will want to use values larger, but not much larger
- * than 1 to both smooth over transient shortages and hedge
- * against uneven progress; as traded off against the cost of
- * extra task overhead. We leave the user to pick a threshold
- * value to compare with the results of this call to guide
- * decisions, but recommend values such as 3.
- *
- * When all threads are active, it is on average OK to estimate
- * surplus strictly locally. In steady-state, if one thread is
- * maintaining say 2 surplus tasks, then so are others. So we can
- * just use estimated queue length (although note that (queueTop -
- * queueBase) can be an overestimate because of stealers lagging
- * increments of queueBase). However, this strategy alone leads
- * to serious mis-estimates in some non-steady-state conditions
- * (ramp-up, ramp-down, other stalls). We can detect many of these
- * by further considering the number of "idle" threads, that are
- * known to have zero queued tasks, so compensate by a factor of
- * (#idle/#active) threads.
- */
- final int getEstimatedSurplusTaskCount() {
- return queueTop - queueBase - pool.idlePerActive();
- }
-
- /**
- * Runs tasks until {@code pool.isQuiescent()}. We piggyback on
- * pool's active count ctl maintenance, but rather than blocking
- * when tasks cannot be found, we rescan until all others cannot
- * find tasks either. The bracketing by pool quiescerCounts
- * updates suppresses pool auto-shutdown mechanics that could
- * otherwise prematurely terminate the pool because all threads
- * appear to be inactive.
- */
- final void helpQuiescePool() {
- boolean active = true;
- ForkJoinTask<?> ps = currentSteal; // to restore below
- ForkJoinPool p = pool;
- p.addQuiescerCount(1);
- for (;;) {
- ForkJoinWorkerThread[] ws = p.workers;
- ForkJoinWorkerThread v = null;
- int n;
- if (queueTop != queueBase)
- v = this;
- else if (ws != null && (n = ws.length) > 1) {
- ForkJoinWorkerThread w;
- int r = nextSeed(); // cheap version of FJP.scan
- int steps = n << 1;
- for (int i = 0; i < steps; ++i) {
- if ((w = ws[(i + r) & (n - 1)]) != null &&
- w.queueBase != w.queueTop) {
- v = w;
- break;
- }
- }
- }
- if (v != null) {
- ForkJoinTask<?> t;
- if (!active) {
- active = true;
- p.addActiveCount(1);
- }
- if ((t = (v != this) ? v.deqTask() :
- locallyFifo ? locallyDeqTask() : popTask()) != null) {
- currentSteal = t;
- t.doExec();
- currentSteal = ps;
- }
- }
- else {
- if (active) {
- active = false;
- p.addActiveCount(-1);
- }
- if (p.isQuiescent()) {
- p.addActiveCount(1);
- p.addQuiescerCount(-1);
- break;
- }
- }
- }
- }
-
- // Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long ABASE;
- private static final int ASHIFT;
-
- static {
- int s;
- try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> a = ForkJoinTask[].class;
- ABASE = UNSAFE.arrayBaseOffset(a);
- s = UNSAFE.arrayIndexScale(a);
- } catch (Exception e) {
- throw new Error(e);
- }
- if ((s & (s-1)) != 0)
- throw new Error("data type scale not a power of two");
- ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
- }
-
}
--- a/jdk/src/share/classes/javax/management/MBeanFeatureInfo.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/javax/management/MBeanFeatureInfo.java Fri Dec 28 18:36:41 2012 -0800
@@ -239,12 +239,10 @@
case 1:
final String[] names = (String[])in.readObject();
- if (names.length == 0) {
- descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR;
- } else {
- final Object[] values = (Object[])in.readObject();
- descriptor = new ImmutableDescriptor(names, values);
- }
+ final Object[] values = (Object[]) in.readObject();
+ descriptor = (names.length == 0) ?
+ ImmutableDescriptor.EMPTY_DESCRIPTOR :
+ new ImmutableDescriptor(names, values);
break;
case 0:
--- a/jdk/src/share/classes/javax/management/MBeanInfo.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/javax/management/MBeanInfo.java Fri Dec 28 18:36:41 2012 -0800
@@ -704,12 +704,10 @@
case 1:
final String[] names = (String[])in.readObject();
- if (names.length == 0) {
- descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR;
- } else {
- final Object[] values = (Object[])in.readObject();
- descriptor = new ImmutableDescriptor(names, values);
- }
+ final Object[] values = (Object[]) in.readObject();
+ descriptor = (names.length == 0) ?
+ ImmutableDescriptor.EMPTY_DESCRIPTOR :
+ new ImmutableDescriptor(names, values);
break;
case 0:
--- a/jdk/src/share/classes/javax/management/remote/JMXConnectorFactory.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/javax/management/remote/JMXConnectorFactory.java Fri Dec 28 18:36:41 2012 -0800
@@ -137,8 +137,10 @@
* JAR conventions for service providers</a>, where the service
* interface is <code>JMXConnectorProvider</code>.</p>
*
- * <p>Every implementation must support the RMI connector protocols,
- * specified with the string <code>rmi</code> or
+ * <p>Every implementation must support the RMI connector protocol with
+ * the default RMI transport, specified with string <code>rmi</code>.
+ * An implementation may optionally support the RMI connector protocol
+ * with the RMI/IIOP transport, specified with the string
* <code>iiop</code>.</p>
*
* <p>Once a provider is found, the result of the
--- a/jdk/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java Fri Dec 28 18:36:41 2012 -0800
@@ -129,8 +129,10 @@
* JAR conventions for service providers</a>, where the service
* interface is <code>JMXConnectorServerProvider</code>.</p>
*
- * <p>Every implementation must support the RMI connector protocols,
- * specified with the string <code>rmi</code> or
+ * <p>Every implementation must support the RMI connector protocol with
+ * the default RMI transport, specified with string <code>rmi</code>.
+ * An implementation may optionally support the RMI connector protocol
+ * with the RMI/IIOP transport, specified with the string
* <code>iiop</code>.</p>
*
* <p>Once a provider is found, the result of the
--- a/jdk/src/share/classes/javax/management/remote/rmi/RMIConnector.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/javax/management/remote/rmi/RMIConnector.java Fri Dec 28 18:36:41 2012 -0800
@@ -238,10 +238,21 @@
//--------------------------------------------------------------------
// implements JMXConnector interface
//--------------------------------------------------------------------
+
+ /**
+ * @throws IOException if the connection could not be made because of a
+ * communication problem, or in the case of the {@code iiop} protocol,
+ * that RMI/IIOP is not supported
+ */
public void connect() throws IOException {
connect(null);
}
+ /**
+ * @throws IOException if the connection could not be made because of a
+ * communication problem, or in the case of the {@code iiop} protocol,
+ * that RMI/IIOP is not supported
+ */
public synchronized void connect(Map<String,?> environment)
throws IOException {
final boolean tracing = logger.traceOn();
--- a/jdk/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java Fri Dec 28 18:36:41 2012 -0800
@@ -337,7 +337,8 @@
* @exception IllegalStateException if the connector server has
* not been attached to an MBean server.
* @exception IOException if the connector server cannot be
- * started.
+ * started, or in the case of the {@code iiop} protocol, that
+ * RMI/IIOP is not supported.
*/
public synchronized void start() throws IOException {
final boolean tracing = logger.traceOn();
--- a/jdk/src/share/classes/javax/management/remote/rmi/package.html Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/javax/management/remote/rmi/package.html Fri Dec 28 18:36:41 2012 -0800
@@ -36,8 +36,8 @@
that different implementations of the RMI connector can
interoperate.</p>
- <p>The RMI connector supports both the JRMP and the IIOP transports
- for RMI.</p>
+ <p>The RMI connector supports the JRMP transport for RMI, and
+ optionally the IIOP transport.</p>
<p>Like most connectors in the JMX Remote API, an RMI connector
usually has an address, which
--- a/jdk/src/share/classes/javax/security/auth/kerberos/package.html Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/javax/security/auth/kerberos/package.html Fri Dec 28 18:36:41 2012 -0800
@@ -43,10 +43,15 @@
You can provide the name of your default realm and Key Distribution
Center (KDC) host for that realm using the system properties
- java.security.krb5.realm and java.security.krb5.kdc. Alternatively, you
- can provide an MIT style configuration file called krb5.conf in
- <java-home>/lib/security. If you place this file elsewhere, you can
- indicate that location via the system property java.security.krb5.conf.<p>
+ {@code java.security.krb5.realm} and {@code java.security.krb5.kdc}.
+ Both properties must be set.
+ Alternatively, the {@code java.security.krb5.conf} system property can
+ be set to the location of an MIT style {@code krb5.conf} configuration
+ file. If none of these system properties are set, the {@code krb5.conf}
+ file is searched for in an implementation-specific manner. Typically,
+ an implementation will first look for a {@code krb5.conf} file in
+ {@code <java-home>/lib/security} and failing that, in an OS-specific
+ location.<p>
<!--
<h2>Package Specification</h2>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/Attributes.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// Attributes.java - attribute list with Namespace support
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the public domain.
+// $Id: Attributes.java,v 1.2 2004/11/03 22:44:51 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+
+/**
+ * Interface for a list of XML attributes.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This interface allows access to a list of attributes in
+ * three different ways:</p>
+ *
+ * <ol>
+ * <li>by attribute index;</li>
+ * <li>by Namespace-qualified name; or</li>
+ * <li>by qualified (prefixed) name.</li>
+ * </ol>
+ *
+ * <p>The list will not contain attributes that were declared
+ * #IMPLIED but not specified in the start tag. It will also not
+ * contain attributes used as Namespace declarations (xmlns*) unless
+ * the <code>http://xml.org/sax/features/namespace-prefixes</code>
+ * feature is set to <var>true</var> (it is <var>false</var> by
+ * default).
+ * Because SAX2 conforms to the original "Namespaces in XML"
+ * recommendation, it normally does not
+ * give namespace declaration attributes a namespace URI.
+ * </p>
+ *
+ * <p>Some SAX2 parsers may support using an optional feature flag
+ * (<code>http://xml.org/sax/features/xmlns-uris</code>) to request
+ * that those attributes be given URIs, conforming to a later
+ * backwards-incompatible revision of that recommendation. (The
+ * attribute's "local name" will be the prefix, or "xmlns" when
+ * defining a default element namespace.) For portability, handler
+ * code should always resolve that conflict, rather than requiring
+ * parsers that can change the setting of that feature flag. </p>
+ *
+ * <p>If the namespace-prefixes feature (see above) is
+ * <var>false</var>, access by qualified name may not be available; if
+ * the <code>http://xml.org/sax/features/namespaces</code> feature is
+ * <var>false</var>, access by Namespace-qualified names may not be
+ * available.</p>
+ *
+ * <p>This interface replaces the now-deprecated SAX1 {@link
+ * org.xml.sax.AttributeList AttributeList} interface, which does not
+ * contain Namespace support. In addition to Namespace support, it
+ * adds the <var>getIndex</var> methods (below).</p>
+ *
+ * <p>The order of attributes in the list is unspecified, and will
+ * vary from implementation to implementation.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson
+ * @see org.xml.sax.helpers.AttributesImpl
+ * @see org.xml.sax.ext.DeclHandler#attributeDecl
+ */
+public interface Attributes
+{
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Indexed access.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Return the number of attributes in the list.
+ *
+ * <p>Once you know the number of attributes, you can iterate
+ * through the list.</p>
+ *
+ * @return The number of attributes in the list.
+ * @see #getURI(int)
+ * @see #getLocalName(int)
+ * @see #getQName(int)
+ * @see #getType(int)
+ * @see #getValue(int)
+ */
+ public abstract int getLength ();
+
+
+ /**
+ * Look up an attribute's Namespace URI by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The Namespace URI, or the empty string if none
+ * is available, or null if the index is out of
+ * range.
+ * @see #getLength
+ */
+ public abstract String getURI (int index);
+
+
+ /**
+ * Look up an attribute's local name by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The local name, or the empty string if Namespace
+ * processing is not being performed, or null
+ * if the index is out of range.
+ * @see #getLength
+ */
+ public abstract String getLocalName (int index);
+
+
+ /**
+ * Look up an attribute's XML qualified (prefixed) name by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The XML qualified name, or the empty string
+ * if none is available, or null if the index
+ * is out of range.
+ * @see #getLength
+ */
+ public abstract String getQName (int index);
+
+
+ /**
+ * Look up an attribute's type by index.
+ *
+ * <p>The attribute type is one of the strings "CDATA", "ID",
+ * "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES",
+ * or "NOTATION" (always in upper case).</p>
+ *
+ * <p>If the parser has not read a declaration for the attribute,
+ * or if the parser does not report attribute types, then it must
+ * return the value "CDATA" as stated in the XML 1.0 Recommendation
+ * (clause 3.3.3, "Attribute-Value Normalization").</p>
+ *
+ * <p>For an enumerated attribute that is not a notation, the
+ * parser will report the type as "NMTOKEN".</p>
+ *
+ * @param index The attribute index (zero-based).
+ * @return The attribute's type as a string, or null if the
+ * index is out of range.
+ * @see #getLength
+ */
+ public abstract String getType (int index);
+
+
+ /**
+ * Look up an attribute's value by index.
+ *
+ * <p>If the attribute value is a list of tokens (IDREFS,
+ * ENTITIES, or NMTOKENS), the tokens will be concatenated
+ * into a single string with each token separated by a
+ * single space.</p>
+ *
+ * @param index The attribute index (zero-based).
+ * @return The attribute's value as a string, or null if the
+ * index is out of range.
+ * @see #getLength
+ */
+ public abstract String getValue (int index);
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Name-based query.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Look up the index of an attribute by Namespace name.
+ *
+ * @param uri The Namespace URI, or the empty string if
+ * the name has no Namespace URI.
+ * @param localName The attribute's local name.
+ * @return The index of the attribute, or -1 if it does not
+ * appear in the list.
+ */
+ public int getIndex (String uri, String localName);
+
+
+ /**
+ * Look up the index of an attribute by XML qualified (prefixed) name.
+ *
+ * @param qName The qualified (prefixed) name.
+ * @return The index of the attribute, or -1 if it does not
+ * appear in the list.
+ */
+ public int getIndex (String qName);
+
+
+ /**
+ * Look up an attribute's type by Namespace name.
+ *
+ * <p>See {@link #getType(int) getType(int)} for a description
+ * of the possible types.</p>
+ *
+ * @param uri The Namespace URI, or the empty String if the
+ * name has no Namespace URI.
+ * @param localName The local name of the attribute.
+ * @return The attribute type as a string, or null if the
+ * attribute is not in the list or if Namespace
+ * processing is not being performed.
+ */
+ public abstract String getType (String uri, String localName);
+
+
+ /**
+ * Look up an attribute's type by XML qualified (prefixed) name.
+ *
+ * <p>See {@link #getType(int) getType(int)} for a description
+ * of the possible types.</p>
+ *
+ * @param qName The XML qualified name.
+ * @return The attribute type as a string, or null if the
+ * attribute is not in the list or if qualified names
+ * are not available.
+ */
+ public abstract String getType (String qName);
+
+
+ /**
+ * Look up an attribute's value by Namespace name.
+ *
+ * <p>See {@link #getValue(int) getValue(int)} for a description
+ * of the possible values.</p>
+ *
+ * @param uri The Namespace URI, or the empty String if the
+ * name has no Namespace URI.
+ * @param localName The local name of the attribute.
+ * @return The attribute value as a string, or null if the
+ * attribute is not in the list.
+ */
+ public abstract String getValue (String uri, String localName);
+
+
+ /**
+ * Look up an attribute's value by XML qualified (prefixed) name.
+ *
+ * <p>See {@link #getValue(int) getValue(int)} for a description
+ * of the possible values.</p>
+ *
+ * @param qName The XML qualified name.
+ * @return The attribute value as a string, or null if the
+ * attribute is not in the list or if qualified names
+ * are not available.
+ */
+ public abstract String getValue (String qName);
+
+}
+
+// end of Attributes.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/ContentHandler.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// ContentHandler.java - handle main document content.
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the public domain.
+// $Id: ContentHandler.java,v 1.2 2004/11/03 22:44:51 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+
+/**
+ * Receive notification of the logical content of a document.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This is the main interface that most SAX applications
+ * implement: if the application needs to be informed of basic parsing
+ * events, it implements this interface and registers an instance with
+ * the SAX parser using the {@link org.xml.sax.XMLReader#setContentHandler
+ * setContentHandler} method. The parser uses the instance to report
+ * basic document-related events like the start and end of elements
+ * and character data.</p>
+ *
+ * <p>The order of events in this interface is very important, and
+ * mirrors the order of information in the document itself. For
+ * example, all of an element's content (character data, processing
+ * instructions, and/or subelements) will appear, in order, between
+ * the startElement event and the corresponding endElement event.</p>
+ *
+ * <p>This interface is similar to the now-deprecated SAX 1.0
+ * DocumentHandler interface, but it adds support for Namespaces
+ * and for reporting skipped entities (in non-validating XML
+ * processors).</p>
+ *
+ * <p>Implementors should note that there is also a
+ * <code>ContentHandler</code> class in the <code>java.net</code>
+ * package; that means that it's probably a bad idea to do</p>
+ *
+ * <pre>import java.net.*;
+ * import org.xml.sax.*;
+ * </pre>
+ *
+ * <p>In fact, "import ...*" is usually a sign of sloppy programming
+ * anyway, so the user should consider this a feature rather than a
+ * bug.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLReader
+ * @see org.xml.sax.DTDHandler
+ * @see org.xml.sax.ErrorHandler
+ */
+public interface ContentHandler
+{
+
+ /**
+ * Receive an object for locating the origin of SAX document events.
+ *
+ * <p>SAX parsers are strongly encouraged (though not absolutely
+ * required) to supply a locator: if it does so, it must supply
+ * the locator to the application by invoking this method before
+ * invoking any of the other methods in the ContentHandler
+ * interface.</p>
+ *
+ * <p>The locator allows the application to determine the end
+ * position of any document-related event, even if the parser is
+ * not reporting an error. Typically, the application will
+ * use this information for reporting its own errors (such as
+ * character content that does not match an application's
+ * business rules). The information returned by the locator
+ * is probably not sufficient for use with a search engine.</p>
+ *
+ * <p>Note that the locator will return correct information only
+ * during the invocation SAX event callbacks after
+ * {@link #startDocument startDocument} returns and before
+ * {@link #endDocument endDocument} is called. The
+ * application should not attempt to use it at any other time.</p>
+ *
+ * @param locator an object that can return the location of
+ * any SAX document event
+ * @see org.xml.sax.Locator
+ */
+ public void setDocumentLocator (Locator locator);
+
+
+ /**
+ * Receive notification of the beginning of a document.
+ *
+ * <p>The SAX parser will invoke this method only once, before any
+ * other event callbacks (except for {@link #setDocumentLocator
+ * setDocumentLocator}).</p>
+ *
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ * @see #endDocument
+ */
+ public void startDocument ()
+ throws SAXException;
+
+
+ /**
+ * Receive notification of the end of a document.
+ *
+ * <p><strong>There is an apparent contradiction between the
+ * documentation for this method and the documentation for {@link
+ * org.xml.sax.ErrorHandler#fatalError}. Until this ambiguity is
+ * resolved in a future major release, clients should make no
+ * assumptions about whether endDocument() will or will not be
+ * invoked when the parser has reported a fatalError() or thrown
+ * an exception.</strong></p>
+ *
+ * <p>The SAX parser will invoke this method only once, and it will
+ * be the last method invoked during the parse. The parser shall
+ * not invoke this method until it has either abandoned parsing
+ * (because of an unrecoverable error) or reached the end of
+ * input.</p>
+ *
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ * @see #startDocument
+ */
+ public void endDocument()
+ throws SAXException;
+
+
+ /**
+ * Begin the scope of a prefix-URI Namespace mapping.
+ *
+ * <p>The information from this event is not necessary for
+ * normal Namespace processing: the SAX XML reader will
+ * automatically replace prefixes for element and attribute
+ * names when the <code>http://xml.org/sax/features/namespaces</code>
+ * feature is <var>true</var> (the default).</p>
+ *
+ * <p>There are cases, however, when applications need to
+ * use prefixes in character data or in attribute values,
+ * where they cannot safely be expanded automatically; the
+ * start/endPrefixMapping event supplies the information
+ * to the application to expand prefixes in those contexts
+ * itself, if necessary.</p>
+ *
+ * <p>Note that start/endPrefixMapping events are not
+ * guaranteed to be properly nested relative to each other:
+ * all startPrefixMapping events will occur immediately before the
+ * corresponding {@link #startElement startElement} event,
+ * and all {@link #endPrefixMapping endPrefixMapping}
+ * events will occur immediately after the corresponding
+ * {@link #endElement endElement} event,
+ * but their order is not otherwise
+ * guaranteed.</p>
+ *
+ * <p>There should never be start/endPrefixMapping events for the
+ * "xml" prefix, since it is predeclared and immutable.</p>
+ *
+ * @param prefix the Namespace prefix being declared.
+ * An empty string is used for the default element namespace,
+ * which has no prefix.
+ * @param uri the Namespace URI the prefix is mapped to
+ * @throws org.xml.sax.SAXException the client may throw
+ * an exception during processing
+ * @see #endPrefixMapping
+ * @see #startElement
+ */
+ public void startPrefixMapping (String prefix, String uri)
+ throws SAXException;
+
+
+ /**
+ * End the scope of a prefix-URI mapping.
+ *
+ * <p>See {@link #startPrefixMapping startPrefixMapping} for
+ * details. These events will always occur immediately after the
+ * corresponding {@link #endElement endElement} event, but the order of
+ * {@link #endPrefixMapping endPrefixMapping} events is not otherwise
+ * guaranteed.</p>
+ *
+ * @param prefix the prefix that was being mapped.
+ * This is the empty string when a default mapping scope ends.
+ * @throws org.xml.sax.SAXException the client may throw
+ * an exception during processing
+ * @see #startPrefixMapping
+ * @see #endElement
+ */
+ public void endPrefixMapping (String prefix)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of the beginning of an element.
+ *
+ * <p>The Parser will invoke this method at the beginning of every
+ * element in the XML document; there will be a corresponding
+ * {@link #endElement endElement} event for every startElement event
+ * (even when the element is empty). All of the element's content will be
+ * reported, in order, before the corresponding endElement
+ * event.</p>
+ *
+ * <p>This event allows up to three name components for each
+ * element:</p>
+ *
+ * <ol>
+ * <li>the Namespace URI;</li>
+ * <li>the local name; and</li>
+ * <li>the qualified (prefixed) name.</li>
+ * </ol>
+ *
+ * <p>Any or all of these may be provided, depending on the
+ * values of the <var>http://xml.org/sax/features/namespaces</var>
+ * and the <var>http://xml.org/sax/features/namespace-prefixes</var>
+ * properties:</p>
+ *
+ * <ul>
+ * <li>the Namespace URI and local name are required when
+ * the namespaces property is <var>true</var> (the default), and are
+ * optional when the namespaces property is <var>false</var> (if one is
+ * specified, both must be);</li>
+ * <li>the qualified name is required when the namespace-prefixes property
+ * is <var>true</var>, and is optional when the namespace-prefixes property
+ * is <var>false</var> (the default).</li>
+ * </ul>
+ *
+ * <p>Note that the attribute list provided will contain only
+ * attributes with explicit values (specified or defaulted):
+ * #IMPLIED attributes will be omitted. The attribute list
+ * will contain attributes used for Namespace declarations
+ * (xmlns* attributes) only if the
+ * <code>http://xml.org/sax/features/namespace-prefixes</code>
+ * property is true (it is false by default, and support for a
+ * true value is optional).</p>
+ *
+ * <p>Like {@link #characters characters()}, attribute values may have
+ * characters that need more than one <code>char</code> value. </p>
+ *
+ * @param uri the Namespace URI, or the empty string if the
+ * element has no Namespace URI or if Namespace
+ * processing is not being performed
+ * @param localName the local name (without prefix), or the
+ * empty string if Namespace processing is not being
+ * performed
+ * @param qName the qualified name (with prefix), or the
+ * empty string if qualified names are not available
+ * @param atts the attributes attached to the element. If
+ * there are no attributes, it shall be an empty
+ * Attributes object. The value of this object after
+ * startElement returns is undefined
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ * @see #endElement
+ * @see org.xml.sax.Attributes
+ * @see org.xml.sax.helpers.AttributesImpl
+ */
+ public void startElement (String uri, String localName,
+ String qName, Attributes atts)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of the end of an element.
+ *
+ * <p>The SAX parser will invoke this method at the end of every
+ * element in the XML document; there will be a corresponding
+ * {@link #startElement startElement} event for every endElement
+ * event (even when the element is empty).</p>
+ *
+ * <p>For information on the names, see startElement.</p>
+ *
+ * @param uri the Namespace URI, or the empty string if the
+ * element has no Namespace URI or if Namespace
+ * processing is not being performed
+ * @param localName the local name (without prefix), or the
+ * empty string if Namespace processing is not being
+ * performed
+ * @param qName the qualified XML name (with prefix), or the
+ * empty string if qualified names are not available
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ */
+ public void endElement (String uri, String localName,
+ String qName)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of character data.
+ *
+ * <p>The Parser will call this method to report each chunk of
+ * character data. SAX parsers may return all contiguous character
+ * data in a single chunk, or they may split it into several
+ * chunks; however, all of the characters in any single event
+ * must come from the same external entity so that the Locator
+ * provides useful information.</p>
+ *
+ * <p>The application must not attempt to read from the array
+ * outside of the specified range.</p>
+ *
+ * <p>Individual characters may consist of more than one Java
+ * <code>char</code> value. There are two important cases where this
+ * happens, because characters can't be represented in just sixteen bits.
+ * In one case, characters are represented in a <em>Surrogate Pair</em>,
+ * using two special Unicode values. Such characters are in the so-called
+ * "Astral Planes", with a code point above U+FFFF. A second case involves
+ * composite characters, such as a base character combining with one or
+ * more accent characters. </p>
+ *
+ * <p> Your code should not assume that algorithms using
+ * <code>char</code>-at-a-time idioms will be working in character
+ * units; in some cases they will split characters. This is relevant
+ * wherever XML permits arbitrary characters, such as attribute values,
+ * processing instruction data, and comments as well as in data reported
+ * from this method. It's also generally relevant whenever Java code
+ * manipulates internationalized text; the issue isn't unique to XML.</p>
+ *
+ * <p>Note that some parsers will report whitespace in element
+ * content using the {@link #ignorableWhitespace ignorableWhitespace}
+ * method rather than this one (validating parsers <em>must</em>
+ * do so).</p>
+ *
+ * @param ch the characters from the XML document
+ * @param start the start position in the array
+ * @param length the number of characters to read from the array
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ * @see #ignorableWhitespace
+ * @see org.xml.sax.Locator
+ */
+ public void characters (char ch[], int start, int length)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of ignorable whitespace in element content.
+ *
+ * <p>Validating Parsers must use this method to report each chunk
+ * of whitespace in element content (see the W3C XML 1.0
+ * recommendation, section 2.10): non-validating parsers may also
+ * use this method if they are capable of parsing and using
+ * content models.</p>
+ *
+ * <p>SAX parsers may return all contiguous whitespace in a single
+ * chunk, or they may split it into several chunks; however, all of
+ * the characters in any single event must come from the same
+ * external entity, so that the Locator provides useful
+ * information.</p>
+ *
+ * <p>The application must not attempt to read from the array
+ * outside of the specified range.</p>
+ *
+ * @param ch the characters from the XML document
+ * @param start the start position in the array
+ * @param length the number of characters to read from the array
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ * @see #characters
+ */
+ public void ignorableWhitespace (char ch[], int start, int length)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of a processing instruction.
+ *
+ * <p>The Parser will invoke this method once for each processing
+ * instruction found: note that processing instructions may occur
+ * before or after the main document element.</p>
+ *
+ * <p>A SAX parser must never report an XML declaration (XML 1.0,
+ * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
+ * using this method.</p>
+ *
+ * <p>Like {@link #characters characters()}, processing instruction
+ * data may have characters that need more than one <code>char</code>
+ * value. </p>
+ *
+ * @param target the processing instruction target
+ * @param data the processing instruction data, or null if
+ * none was supplied. The data does not include any
+ * whitespace separating it from the target
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ */
+ public void processingInstruction (String target, String data)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of a skipped entity.
+ * This is not called for entity references within markup constructs
+ * such as element start tags or markup declarations. (The XML
+ * recommendation requires reporting skipped external entities.
+ * SAX also reports internal entity expansion/non-expansion, except
+ * within markup constructs.)
+ *
+ * <p>The Parser will invoke this method each time the entity is
+ * skipped. Non-validating processors may skip entities if they
+ * have not seen the declarations (because, for example, the
+ * entity was declared in an external DTD subset). All processors
+ * may skip external entities, depending on the values of the
+ * <code>http://xml.org/sax/features/external-general-entities</code>
+ * and the
+ * <code>http://xml.org/sax/features/external-parameter-entities</code>
+ * properties.</p>
+ *
+ * @param name the name of the skipped entity. If it is a
+ * parameter entity, the name will begin with '%', and if
+ * it is the external DTD subset, it will be the string
+ * "[dtd]"
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ */
+ public void skippedEntity (String name)
+ throws SAXException;
+}
+
+// end of ContentHandler.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/DTDHandler.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// SAX DTD handler.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: DTDHandler.java,v 1.2 2004/11/03 22:44:51 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+/**
+ * Receive notification of basic DTD-related events.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>If a SAX application needs information about notations and
+ * unparsed entities, then the application implements this
+ * interface and registers an instance with the SAX parser using
+ * the parser's setDTDHandler method. The parser uses the
+ * instance to report notation and unparsed entity declarations to
+ * the application.</p>
+ *
+ * <p>Note that this interface includes only those DTD events that
+ * the XML recommendation <em>requires</em> processors to report:
+ * notation and unparsed entity declarations.</p>
+ *
+ * <p>The SAX parser may report these events in any order, regardless
+ * of the order in which the notations and unparsed entities were
+ * declared; however, all DTD events must be reported after the
+ * document handler's startDocument event, and before the first
+ * startElement event.
+ * (If the {@link org.xml.sax.ext.LexicalHandler LexicalHandler} is
+ * used, these events must also be reported before the endDTD event.)
+ * </p>
+ *
+ * <p>It is up to the application to store the information for
+ * future use (perhaps in a hash table or object tree).
+ * If the application encounters attributes of type "NOTATION",
+ * "ENTITY", or "ENTITIES", it can use the information that it
+ * obtained through this interface to find the entity and/or
+ * notation corresponding with the attribute value.</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLReader#setDTDHandler
+ */
+public interface DTDHandler {
+
+
+ /**
+ * Receive notification of a notation declaration event.
+ *
+ * <p>It is up to the application to record the notation for later
+ * reference, if necessary;
+ * notations may appear as attribute values and in unparsed entity
+ * declarations, and are sometime used with processing instruction
+ * target names.</p>
+ *
+ * <p>At least one of publicId and systemId must be non-null.
+ * If a system identifier is present, and it is a URL, the SAX
+ * parser must resolve it fully before passing it to the
+ * application through this event.</p>
+ *
+ * <p>There is no guarantee that the notation declaration will be
+ * reported before any unparsed entities that use it.</p>
+ *
+ * @param name The notation name.
+ * @param publicId The notation's public identifier, or null if
+ * none was given.
+ * @param systemId The notation's system identifier, or null if
+ * none was given.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #unparsedEntityDecl
+ * @see org.xml.sax.Attributes
+ */
+ public abstract void notationDecl (String name,
+ String publicId,
+ String systemId)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of an unparsed entity declaration event.
+ *
+ * <p>Note that the notation name corresponds to a notation
+ * reported by the {@link #notationDecl notationDecl} event.
+ * It is up to the application to record the entity for later
+ * reference, if necessary;
+ * unparsed entities may appear as attribute values.
+ * </p>
+ *
+ * <p>If the system identifier is a URL, the parser must resolve it
+ * fully before passing it to the application.</p>
+ *
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @param name The unparsed entity's name.
+ * @param publicId The entity's public identifier, or null if none
+ * was given.
+ * @param systemId The entity's system identifier.
+ * @param notationName The name of the associated notation.
+ * @see #notationDecl
+ * @see org.xml.sax.Attributes
+ */
+ public abstract void unparsedEntityDecl (String name,
+ String publicId,
+ String systemId,
+ String notationName)
+ throws SAXException;
+
+}
+
+// end of DTDHandler.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/EntityResolver.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// SAX entity resolver.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: EntityResolver.java,v 1.2 2004/11/03 22:44:52 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+import java.io.IOException;
+
+
+/**
+ * Basic interface for resolving entities.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>If a SAX application needs to implement customized handling
+ * for external entities, it must implement this interface and
+ * register an instance with the SAX driver using the
+ * {@link org.xml.sax.XMLReader#setEntityResolver setEntityResolver}
+ * method.</p>
+ *
+ * <p>The XML reader will then allow the application to intercept any
+ * external entities (including the external DTD subset and external
+ * parameter entities, if any) before including them.</p>
+ *
+ * <p>Many SAX applications will not need to implement this interface,
+ * but it will be especially useful for applications that build
+ * XML documents from databases or other specialised input sources,
+ * or for applications that use URI types other than URLs.</p>
+ *
+ * <p>The following resolver would provide the application
+ * with a special character stream for the entity with the system
+ * identifier "http://www.myhost.com/today":</p>
+ *
+ * <pre>
+ * import org.xml.sax.EntityResolver;
+ * import org.xml.sax.InputSource;
+ *
+ * public class MyResolver implements EntityResolver {
+ * public InputSource resolveEntity (String publicId, String systemId)
+ * {
+ * if (systemId.equals("http://www.myhost.com/today")) {
+ * // return a special input source
+ * MyReader reader = new MyReader();
+ * return new InputSource(reader);
+ * } else {
+ * // use the default behaviour
+ * return null;
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * <p>The application can also use this interface to redirect system
+ * identifiers to local URIs or to look up replacements in a catalog
+ * (possibly by using the public identifier).</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLReader#setEntityResolver
+ * @see org.xml.sax.InputSource
+ */
+public interface EntityResolver {
+
+
+ /**
+ * Allow the application to resolve external entities.
+ *
+ * <p>The parser will call this method before opening any external
+ * entity except the top-level document entity. Such entities include
+ * the external DTD subset and external parameter entities referenced
+ * within the DTD (in either case, only if the parser reads external
+ * parameter entities), and external general entities referenced
+ * within the document element (if the parser reads external general
+ * entities). The application may request that the parser locate
+ * the entity itself, that it use an alternative URI, or that it
+ * use data provided by the application (as a character or byte
+ * input stream).</p>
+ *
+ * <p>Application writers can use this method to redirect external
+ * system identifiers to secure and/or local URIs, to look up
+ * public identifiers in a catalogue, or to read an entity from a
+ * database or other input source (including, for example, a dialog
+ * box). Neither XML nor SAX specifies a preferred policy for using
+ * public or system IDs to resolve resources. However, SAX specifies
+ * how to interpret any InputSource returned by this method, and that
+ * if none is returned, then the system ID will be dereferenced as
+ * a URL. </p>
+ *
+ * <p>If the system identifier is a URL, the SAX parser must
+ * resolve it fully before reporting it to the application.</p>
+ *
+ * @param publicId The public identifier of the external entity
+ * being referenced, or null if none was supplied.
+ * @param systemId The system identifier of the external entity
+ * being referenced.
+ * @return An InputSource object describing the new input source,
+ * or null to request that the parser open a regular
+ * URI connection to the system identifier.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @exception java.io.IOException A Java-specific IO exception,
+ * possibly the result of creating a new InputStream
+ * or Reader for the InputSource.
+ * @see org.xml.sax.InputSource
+ */
+ public abstract InputSource resolveEntity (String publicId,
+ String systemId)
+ throws SAXException, IOException;
+
+}
+
+// end of EntityResolver.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/ErrorHandler.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// SAX error handler.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: ErrorHandler.java,v 1.2 2004/11/03 22:44:52 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+
+/**
+ * Basic interface for SAX error handlers.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>If a SAX application needs to implement customized error
+ * handling, it must implement this interface and then register an
+ * instance with the XML reader using the
+ * {@link org.xml.sax.XMLReader#setErrorHandler setErrorHandler}
+ * method. The parser will then report all errors and warnings
+ * through this interface.</p>
+ *
+ * <p><strong>WARNING:</strong> If an application does <em>not</em>
+ * register an ErrorHandler, XML parsing errors will go unreported,
+ * except that <em>SAXParseException</em>s will be thrown for fatal errors.
+ * In order to detect validity errors, an ErrorHandler that does something
+ * with {@link #error error()} calls must be registered.</p>
+ *
+ * <p>For XML processing errors, a SAX driver must use this interface
+ * in preference to throwing an exception: it is up to the application
+ * to decide whether to throw an exception for different types of
+ * errors and warnings. Note, however, that there is no requirement that
+ * the parser continue to report additional errors after a call to
+ * {@link #fatalError fatalError}. In other words, a SAX driver class
+ * may throw an exception after reporting any fatalError.
+ * Also parsers may throw appropriate exceptions for non-XML errors.
+ * For example, {@link XMLReader#parse XMLReader.parse()} would throw
+ * an IOException for errors accessing entities or the document.</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLReader#setErrorHandler
+ * @see org.xml.sax.SAXParseException
+ */
+public interface ErrorHandler {
+
+
+ /**
+ * Receive notification of a warning.
+ *
+ * <p>SAX parsers will use this method to report conditions that
+ * are not errors or fatal errors as defined by the XML
+ * recommendation. The default behaviour is to take no
+ * action.</p>
+ *
+ * <p>The SAX parser must continue to provide normal parsing events
+ * after invoking this method: it should still be possible for the
+ * application to process the document through to the end.</p>
+ *
+ * <p>Filters may use this method to report other, non-XML warnings
+ * as well.</p>
+ *
+ * @param exception The warning information encapsulated in a
+ * SAX parse exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.SAXParseException
+ */
+ public abstract void warning (SAXParseException exception)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of a recoverable error.
+ *
+ * <p>This corresponds to the definition of "error" in section 1.2
+ * of the W3C XML 1.0 Recommendation. For example, a validating
+ * parser would use this callback to report the violation of a
+ * validity constraint. The default behaviour is to take no
+ * action.</p>
+ *
+ * <p>The SAX parser must continue to provide normal parsing
+ * events after invoking this method: it should still be possible
+ * for the application to process the document through to the end.
+ * If the application cannot do so, then the parser should report
+ * a fatal error even if the XML recommendation does not require
+ * it to do so.</p>
+ *
+ * <p>Filters may use this method to report other, non-XML errors
+ * as well.</p>
+ *
+ * @param exception The error information encapsulated in a
+ * SAX parse exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.SAXParseException
+ */
+ public abstract void error (SAXParseException exception)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of a non-recoverable error.
+ *
+ * <p><strong>There is an apparent contradiction between the
+ * documentation for this method and the documentation for {@link
+ * org.xml.sax.ContentHandler#endDocument}. Until this ambiguity
+ * is resolved in a future major release, clients should make no
+ * assumptions about whether endDocument() will or will not be
+ * invoked when the parser has reported a fatalError() or thrown
+ * an exception.</strong></p>
+ *
+ * <p>This corresponds to the definition of "fatal error" in
+ * section 1.2 of the W3C XML 1.0 Recommendation. For example, a
+ * parser would use this callback to report the violation of a
+ * well-formedness constraint.</p>
+ *
+ * <p>The application must assume that the document is unusable
+ * after the parser has invoked this method, and should continue
+ * (if at all) only for the sake of collecting additional error
+ * messages: in fact, SAX parsers are free to stop reporting any
+ * other events once this method has been invoked.</p>
+ *
+ * @param exception The error information encapsulated in a
+ * SAX parse exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.SAXParseException
+ */
+ public abstract void fatalError (SAXParseException exception)
+ throws SAXException;
+
+}
+
+// end of ErrorHandler.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/InputSource.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// SAX input source.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: InputSource.java,v 1.2 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+import java.io.Reader;
+import java.io.InputStream;
+
+/**
+ * A single input source for an XML entity.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This class allows a SAX application to encapsulate information
+ * about an input source in a single object, which may include
+ * a public identifier, a system identifier, a byte stream (possibly
+ * with a specified encoding), and/or a character stream.</p>
+ *
+ * <p>There are two places that the application can deliver an
+ * input source to the parser: as the argument to the Parser.parse
+ * method, or as the return value of the EntityResolver.resolveEntity
+ * method.</p>
+ *
+ * <p>The SAX parser will use the InputSource object to determine how
+ * to read XML input. If there is a character stream available, the
+ * parser will read that stream directly, disregarding any text
+ * encoding declaration found in that stream.
+ * If there is no character stream, but there is
+ * a byte stream, the parser will use that byte stream, using the
+ * encoding specified in the InputSource or else (if no encoding is
+ * specified) autodetecting the character encoding using an algorithm
+ * such as the one in the XML specification. If neither a character
+ * stream nor a
+ * byte stream is available, the parser will attempt to open a URI
+ * connection to the resource identified by the system
+ * identifier.</p>
+ *
+ * <p>An InputSource object belongs to the application: the SAX parser
+ * shall never modify it in any way (it may modify a copy if
+ * necessary). However, standard processing of both byte and
+ * character streams is to close them on as part of end-of-parse cleanup,
+ * so applications should not attempt to re-use such streams after they
+ * have been handed to a parser. </p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLReader#parse(org.xml.sax.InputSource)
+ * @see org.xml.sax.EntityResolver#resolveEntity
+ * @see java.io.InputStream
+ * @see java.io.Reader
+ */
+public class InputSource {
+
+ /**
+ * Zero-argument default constructor.
+ *
+ * @see #setPublicId
+ * @see #setSystemId
+ * @see #setByteStream
+ * @see #setCharacterStream
+ * @see #setEncoding
+ */
+ public InputSource ()
+ {
+ }
+
+
+ /**
+ * Create a new input source with a system identifier.
+ *
+ * <p>Applications may use setPublicId to include a
+ * public identifier as well, or setEncoding to specify
+ * the character encoding, if known.</p>
+ *
+ * <p>If the system identifier is a URL, it must be fully
+ * resolved (it may not be a relative URL).</p>
+ *
+ * @param systemId The system identifier (URI).
+ * @see #setPublicId
+ * @see #setSystemId
+ * @see #setByteStream
+ * @see #setEncoding
+ * @see #setCharacterStream
+ */
+ public InputSource (String systemId)
+ {
+ setSystemId(systemId);
+ }
+
+
+ /**
+ * Create a new input source with a byte stream.
+ *
+ * <p>Application writers should use setSystemId() to provide a base
+ * for resolving relative URIs, may use setPublicId to include a
+ * public identifier, and may use setEncoding to specify the object's
+ * character encoding.</p>
+ *
+ * @param byteStream The raw byte stream containing the document.
+ * @see #setPublicId
+ * @see #setSystemId
+ * @see #setEncoding
+ * @see #setByteStream
+ * @see #setCharacterStream
+ */
+ public InputSource (InputStream byteStream)
+ {
+ setByteStream(byteStream);
+ }
+
+
+ /**
+ * Create a new input source with a character stream.
+ *
+ * <p>Application writers should use setSystemId() to provide a base
+ * for resolving relative URIs, and may use setPublicId to include a
+ * public identifier.</p>
+ *
+ * <p>The character stream shall not include a byte order mark.</p>
+ *
+ * @see #setPublicId
+ * @see #setSystemId
+ * @see #setByteStream
+ * @see #setCharacterStream
+ */
+ public InputSource (Reader characterStream)
+ {
+ setCharacterStream(characterStream);
+ }
+
+
+ /**
+ * Set the public identifier for this input source.
+ *
+ * <p>The public identifier is always optional: if the application
+ * writer includes one, it will be provided as part of the
+ * location information.</p>
+ *
+ * @param publicId The public identifier as a string.
+ * @see #getPublicId
+ * @see org.xml.sax.Locator#getPublicId
+ * @see org.xml.sax.SAXParseException#getPublicId
+ */
+ public void setPublicId (String publicId)
+ {
+ this.publicId = publicId;
+ }
+
+
+ /**
+ * Get the public identifier for this input source.
+ *
+ * @return The public identifier, or null if none was supplied.
+ * @see #setPublicId
+ */
+ public String getPublicId ()
+ {
+ return publicId;
+ }
+
+
+ /**
+ * Set the system identifier for this input source.
+ *
+ * <p>The system identifier is optional if there is a byte stream
+ * or a character stream, but it is still useful to provide one,
+ * since the application can use it to resolve relative URIs
+ * and can include it in error messages and warnings (the parser
+ * will attempt to open a connection to the URI only if
+ * there is no byte stream or character stream specified).</p>
+ *
+ * <p>If the application knows the character encoding of the
+ * object pointed to by the system identifier, it can register
+ * the encoding using the setEncoding method.</p>
+ *
+ * <p>If the system identifier is a URL, it must be fully
+ * resolved (it may not be a relative URL).</p>
+ *
+ * @param systemId The system identifier as a string.
+ * @see #setEncoding
+ * @see #getSystemId
+ * @see org.xml.sax.Locator#getSystemId
+ * @see org.xml.sax.SAXParseException#getSystemId
+ */
+ public void setSystemId (String systemId)
+ {
+ this.systemId = systemId;
+ }
+
+
+ /**
+ * Get the system identifier for this input source.
+ *
+ * <p>The getEncoding method will return the character encoding
+ * of the object pointed to, or null if unknown.</p>
+ *
+ * <p>If the system ID is a URL, it will be fully resolved.</p>
+ *
+ * @return The system identifier, or null if none was supplied.
+ * @see #setSystemId
+ * @see #getEncoding
+ */
+ public String getSystemId ()
+ {
+ return systemId;
+ }
+
+
+ /**
+ * Set the byte stream for this input source.
+ *
+ * <p>The SAX parser will ignore this if there is also a character
+ * stream specified, but it will use a byte stream in preference
+ * to opening a URI connection itself.</p>
+ *
+ * <p>If the application knows the character encoding of the
+ * byte stream, it should set it with the setEncoding method.</p>
+ *
+ * @param byteStream A byte stream containing an XML document or
+ * other entity.
+ * @see #setEncoding
+ * @see #getByteStream
+ * @see #getEncoding
+ * @see java.io.InputStream
+ */
+ public void setByteStream (InputStream byteStream)
+ {
+ this.byteStream = byteStream;
+ }
+
+
+ /**
+ * Get the byte stream for this input source.
+ *
+ * <p>The getEncoding method will return the character
+ * encoding for this byte stream, or null if unknown.</p>
+ *
+ * @return The byte stream, or null if none was supplied.
+ * @see #getEncoding
+ * @see #setByteStream
+ */
+ public InputStream getByteStream ()
+ {
+ return byteStream;
+ }
+
+
+ /**
+ * Set the character encoding, if known.
+ *
+ * <p>The encoding must be a string acceptable for an
+ * XML encoding declaration (see section 4.3.3 of the XML 1.0
+ * recommendation).</p>
+ *
+ * <p>This method has no effect when the application provides a
+ * character stream.</p>
+ *
+ * @param encoding A string describing the character encoding.
+ * @see #setSystemId
+ * @see #setByteStream
+ * @see #getEncoding
+ */
+ public void setEncoding (String encoding)
+ {
+ this.encoding = encoding;
+ }
+
+
+ /**
+ * Get the character encoding for a byte stream or URI.
+ * This value will be ignored when the application provides a
+ * character stream.
+ *
+ * @return The encoding, or null if none was supplied.
+ * @see #setByteStream
+ * @see #getSystemId
+ * @see #getByteStream
+ */
+ public String getEncoding ()
+ {
+ return encoding;
+ }
+
+
+ /**
+ * Set the character stream for this input source.
+ *
+ * <p>If there is a character stream specified, the SAX parser
+ * will ignore any byte stream and will not attempt to open
+ * a URI connection to the system identifier.</p>
+ *
+ * @param characterStream The character stream containing the
+ * XML document or other entity.
+ * @see #getCharacterStream
+ * @see java.io.Reader
+ */
+ public void setCharacterStream (Reader characterStream)
+ {
+ this.characterStream = characterStream;
+ }
+
+
+ /**
+ * Get the character stream for this input source.
+ *
+ * @return The character stream, or null if none was supplied.
+ * @see #setCharacterStream
+ */
+ public Reader getCharacterStream ()
+ {
+ return characterStream;
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Internal state.
+ ////////////////////////////////////////////////////////////////////
+
+ private String publicId;
+ private String systemId;
+ private InputStream byteStream;
+ private String encoding;
+ private Reader characterStream;
+
+}
+
+// end of InputSource.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/Locator.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// SAX locator interface for document events.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: Locator.java,v 1.2 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+
+/**
+ * Interface for associating a SAX event with a document location.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>If a SAX parser provides location information to the SAX
+ * application, it does so by implementing this interface and then
+ * passing an instance to the application using the content
+ * handler's {@link org.xml.sax.ContentHandler#setDocumentLocator
+ * setDocumentLocator} method. The application can use the
+ * object to obtain the location of any other SAX event
+ * in the XML source document.</p>
+ *
+ * <p>Note that the results returned by the object will be valid only
+ * during the scope of each callback method: the application
+ * will receive unpredictable results if it attempts to use the
+ * locator at any other time, or after parsing completes.</p>
+ *
+ * <p>SAX parsers are not required to supply a locator, but they are
+ * very strongly encouraged to do so. If the parser supplies a
+ * locator, it must do so before reporting any other document events.
+ * If no locator has been set by the time the application receives
+ * the {@link org.xml.sax.ContentHandler#startDocument startDocument}
+ * event, the application should assume that a locator is not
+ * available.</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @see org.xml.sax.ContentHandler#setDocumentLocator
+ */
+public interface Locator {
+
+
+ /**
+ * Return the public identifier for the current document event.
+ *
+ * <p>The return value is the public identifier of the document
+ * entity or of the external parsed entity in which the markup
+ * triggering the event appears.</p>
+ *
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ * @see #getSystemId
+ */
+ public abstract String getPublicId ();
+
+
+ /**
+ * Return the system identifier for the current document event.
+ *
+ * <p>The return value is the system identifier of the document
+ * entity or of the external parsed entity in which the markup
+ * triggering the event appears.</p>
+ *
+ * <p>If the system identifier is a URL, the parser must resolve it
+ * fully before passing it to the application. For example, a file
+ * name must always be provided as a <em>file:...</em> URL, and other
+ * kinds of relative URI are also resolved against their bases.</p>
+ *
+ * @return A string containing the system identifier, or null
+ * if none is available.
+ * @see #getPublicId
+ */
+ public abstract String getSystemId ();
+
+
+ /**
+ * Return the line number where the current document event ends.
+ * Lines are delimited by line ends, which are defined in
+ * the XML specification.
+ *
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of diagnostics;
+ * it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.
+ * In some cases, these "line" numbers match what would be displayed
+ * as columns, and in others they may not match the source text
+ * due to internal entity expansion. </p>
+ *
+ * <p>The return value is an approximation of the line number
+ * in the document entity or external parsed entity where the
+ * markup triggering the event appears.</p>
+ *
+ * <p>If possible, the SAX driver should provide the line position
+ * of the first character after the text associated with the document
+ * event. The first line is line 1.</p>
+ *
+ * @return The line number, or -1 if none is available.
+ * @see #getColumnNumber
+ */
+ public abstract int getLineNumber ();
+
+
+ /**
+ * Return the column number where the current document event ends.
+ * This is one-based number of Java <code>char</code> values since
+ * the last line end.
+ *
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of diagnostics;
+ * it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.
+ * For example, when lines contain combining character sequences, wide
+ * characters, surrogate pairs, or bi-directional text, the value may
+ * not correspond to the column in a text editor's display. </p>
+ *
+ * <p>The return value is an approximation of the column number
+ * in the document entity or external parsed entity where the
+ * markup triggering the event appears.</p>
+ *
+ * <p>If possible, the SAX driver should provide the line position
+ * of the first character after the text associated with the document
+ * event. The first column in each line is column 1.</p>
+ *
+ * @return The column number, or -1 if none is available.
+ * @see #getLineNumber
+ */
+ public abstract int getColumnNumber ();
+
+}
+
+// end of Locator.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/SAXException.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// SAX exception class.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: SAXException.java,v 1.3 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+/**
+ * Encapsulate a general SAX error or warning.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This class can contain basic error or warning information from
+ * either the XML parser or the application: a parser writer or
+ * application writer can subclass it to provide additional
+ * functionality. SAX handlers may throw this exception or
+ * any exception subclassed from it.</p>
+ *
+ * <p>If the application needs to pass through other types of
+ * exceptions, it must wrap those exceptions in a SAXException
+ * or an exception derived from a SAXException.</p>
+ *
+ * <p>If the parser or application needs to include information about a
+ * specific location in an XML document, it should use the
+ * {@link org.xml.sax.SAXParseException SAXParseException} subclass.</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @version 2.0.1 (sax2r2)
+ * @see org.xml.sax.SAXParseException
+ */
+public class SAXException extends Exception {
+
+
+ /**
+ * Create a new SAXException.
+ */
+ public SAXException ()
+ {
+ super();
+ this.exception = null;
+ }
+
+
+ /**
+ * Create a new SAXException.
+ *
+ * @param message The error or warning message.
+ */
+ public SAXException (String message) {
+ super(message);
+ this.exception = null;
+ }
+
+
+ /**
+ * Create a new SAXException wrapping an existing exception.
+ *
+ * <p>The existing exception will be embedded in the new
+ * one, and its message will become the default message for
+ * the SAXException.</p>
+ *
+ * @param e The exception to be wrapped in a SAXException.
+ */
+ public SAXException (Exception e)
+ {
+ super();
+ this.exception = e;
+ }
+
+
+ /**
+ * Create a new SAXException from an existing exception.
+ *
+ * <p>The existing exception will be embedded in the new
+ * one, but the new exception will have its own message.</p>
+ *
+ * @param message The detail message.
+ * @param e The exception to be wrapped in a SAXException.
+ */
+ public SAXException (String message, Exception e)
+ {
+ super(message);
+ this.exception = e;
+ }
+
+
+ /**
+ * Return a detail message for this exception.
+ *
+ * <p>If there is an embedded exception, and if the SAXException
+ * has no detail message of its own, this method will return
+ * the detail message from the embedded exception.</p>
+ *
+ * @return The error or warning message.
+ */
+ public String getMessage ()
+ {
+ String message = super.getMessage();
+
+ if (message == null && exception != null) {
+ return exception.getMessage();
+ } else {
+ return message;
+ }
+ }
+
+
+ /**
+ * Return the embedded exception, if any.
+ *
+ * @return The embedded exception, or null if there is none.
+ */
+ public Exception getException ()
+ {
+ return exception;
+ }
+
+ /**
+ * Return the cause of the exception
+ *
+ * @return Return the cause of the exception
+ */
+ public Throwable getCause() {
+ return exception;
+ }
+
+ /**
+ * Override toString to pick up any embedded exception.
+ *
+ * @return A string representation of this exception.
+ */
+ public String toString ()
+ {
+ if (exception != null) {
+ return super.toString() + "\n" + exception.toString();
+ } else {
+ return super.toString();
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Internal state.
+ //////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * @serial The embedded exception if tunnelling, or null.
+ */
+ private Exception exception;
+
+ // Added serialVersionUID to preserve binary compatibility
+ static final long serialVersionUID = 583241635256073760L;
+}
+
+// end of SAXException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/SAXNotRecognizedException.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// SAXNotRecognizedException.java - unrecognized feature or value.
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the Public Domain.
+// $Id: SAXNotRecognizedException.java,v 1.3 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+
+/**
+ * Exception class for an unrecognized identifier.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>An XMLReader will throw this exception when it finds an
+ * unrecognized feature or property identifier; SAX applications and
+ * extensions may use this class for other, similar purposes.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson
+ * @see org.xml.sax.SAXNotSupportedException
+ */
+public class SAXNotRecognizedException extends SAXException
+{
+
+ /**
+ * Default constructor.
+ */
+ public SAXNotRecognizedException ()
+ {
+ super();
+ }
+
+
+ /**
+ * Construct a new exception with the given message.
+ *
+ * @param message The text message of the exception.
+ */
+ public SAXNotRecognizedException (String message)
+ {
+ super(message);
+ }
+
+ // Added serialVersionUID to preserve binary compatibility
+ static final long serialVersionUID = 5440506620509557213L;
+}
+
+// end of SAXNotRecognizedException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/SAXNotSupportedException.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// SAXNotSupportedException.java - unsupported feature or value.
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the Public Domain.
+// $Id: SAXNotSupportedException.java,v 1.4 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+/**
+ * Exception class for an unsupported operation.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>An XMLReader will throw this exception when it recognizes a
+ * feature or property identifier, but cannot perform the requested
+ * operation (setting a state or value). Other SAX2 applications and
+ * extensions may use this class for similar purposes.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson
+ * @see org.xml.sax.SAXNotRecognizedException
+ */
+public class SAXNotSupportedException extends SAXException
+{
+
+ /**
+ * Construct a new exception with no message.
+ */
+ public SAXNotSupportedException ()
+ {
+ super();
+ }
+
+
+ /**
+ * Construct a new exception with the given message.
+ *
+ * @param message The text message of the exception.
+ */
+ public SAXNotSupportedException (String message)
+ {
+ super(message);
+ }
+
+ // Added serialVersionUID to preserve binary compatibility
+ static final long serialVersionUID = -1422818934641823846L;
+}
+
+// end of SAXNotSupportedException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/SAXParseException.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// SAX exception class.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: SAXParseException.java,v 1.2 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+/**
+ * Encapsulate an XML parse error or warning.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This exception may include information for locating the error
+ * in the original XML document, as if it came from a {@link Locator}
+ * object. Note that although the application
+ * will receive a SAXParseException as the argument to the handlers
+ * in the {@link org.xml.sax.ErrorHandler ErrorHandler} interface,
+ * the application is not actually required to throw the exception;
+ * instead, it can simply read the information in it and take a
+ * different action.</p>
+ *
+ * <p>Since this exception is a subclass of {@link org.xml.sax.SAXException
+ * SAXException}, it inherits the ability to wrap another exception.</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @version 2.0.1 (sax2r2)
+ * @see org.xml.sax.SAXException
+ * @see org.xml.sax.Locator
+ * @see org.xml.sax.ErrorHandler
+ */
+public class SAXParseException extends SAXException {
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Constructors.
+ //////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Create a new SAXParseException from a message and a Locator.
+ *
+ * <p>This constructor is especially useful when an application is
+ * creating its own exception from within a {@link org.xml.sax.ContentHandler
+ * ContentHandler} callback.</p>
+ *
+ * @param message The error or warning message.
+ * @param locator The locator object for the error or warning (may be
+ * null).
+ * @see org.xml.sax.Locator
+ */
+ public SAXParseException (String message, Locator locator) {
+ super(message);
+ if (locator != null) {
+ init(locator.getPublicId(), locator.getSystemId(),
+ locator.getLineNumber(), locator.getColumnNumber());
+ } else {
+ init(null, null, -1, -1);
+ }
+ }
+
+
+ /**
+ * Wrap an existing exception in a SAXParseException.
+ *
+ * <p>This constructor is especially useful when an application is
+ * creating its own exception from within a {@link org.xml.sax.ContentHandler
+ * ContentHandler} callback, and needs to wrap an existing exception that is not a
+ * subclass of {@link org.xml.sax.SAXException SAXException}.</p>
+ *
+ * @param message The error or warning message, or null to
+ * use the message from the embedded exception.
+ * @param locator The locator object for the error or warning (may be
+ * null).
+ * @param e Any exception.
+ * @see org.xml.sax.Locator
+ */
+ public SAXParseException (String message, Locator locator,
+ Exception e) {
+ super(message, e);
+ if (locator != null) {
+ init(locator.getPublicId(), locator.getSystemId(),
+ locator.getLineNumber(), locator.getColumnNumber());
+ } else {
+ init(null, null, -1, -1);
+ }
+ }
+
+
+ /**
+ * Create a new SAXParseException.
+ *
+ * <p>This constructor is most useful for parser writers.</p>
+ *
+ * <p>All parameters except the message are as if
+ * they were provided by a {@link Locator}. For example, if the
+ * system identifier is a URL (including relative filename), the
+ * caller must resolve it fully before creating the exception.</p>
+ *
+ *
+ * @param message The error or warning message.
+ * @param publicId The public identifier of the entity that generated
+ * the error or warning.
+ * @param systemId The system identifier of the entity that generated
+ * the error or warning.
+ * @param lineNumber The line number of the end of the text that
+ * caused the error or warning.
+ * @param columnNumber The column number of the end of the text that
+ * cause the error or warning.
+ */
+ public SAXParseException (String message, String publicId, String systemId,
+ int lineNumber, int columnNumber)
+ {
+ super(message);
+ init(publicId, systemId, lineNumber, columnNumber);
+ }
+
+
+ /**
+ * Create a new SAXParseException with an embedded exception.
+ *
+ * <p>This constructor is most useful for parser writers who
+ * need to wrap an exception that is not a subclass of
+ * {@link org.xml.sax.SAXException SAXException}.</p>
+ *
+ * <p>All parameters except the message and exception are as if
+ * they were provided by a {@link Locator}. For example, if the
+ * system identifier is a URL (including relative filename), the
+ * caller must resolve it fully before creating the exception.</p>
+ *
+ * @param message The error or warning message, or null to use
+ * the message from the embedded exception.
+ * @param publicId The public identifier of the entity that generated
+ * the error or warning.
+ * @param systemId The system identifier of the entity that generated
+ * the error or warning.
+ * @param lineNumber The line number of the end of the text that
+ * caused the error or warning.
+ * @param columnNumber The column number of the end of the text that
+ * cause the error or warning.
+ * @param e Another exception to embed in this one.
+ */
+ public SAXParseException (String message, String publicId, String systemId,
+ int lineNumber, int columnNumber, Exception e)
+ {
+ super(message, e);
+ init(publicId, systemId, lineNumber, columnNumber);
+ }
+
+
+ /**
+ * Internal initialization method.
+ *
+ * @param publicId The public identifier of the entity which generated the exception,
+ * or null.
+ * @param systemId The system identifier of the entity which generated the exception,
+ * or null.
+ * @param lineNumber The line number of the error, or -1.
+ * @param columnNumber The column number of the error, or -1.
+ */
+ private void init (String publicId, String systemId,
+ int lineNumber, int columnNumber)
+ {
+ this.publicId = publicId;
+ this.systemId = systemId;
+ this.lineNumber = lineNumber;
+ this.columnNumber = columnNumber;
+ }
+
+
+ /**
+ * Get the public identifier of the entity where the exception occurred.
+ *
+ * @return A string containing the public identifier, or null
+ * if none is available.
+ * @see org.xml.sax.Locator#getPublicId
+ */
+ public String getPublicId ()
+ {
+ return this.publicId;
+ }
+
+
+ /**
+ * Get the system identifier of the entity where the exception occurred.
+ *
+ * <p>If the system identifier is a URL, it will have been resolved
+ * fully.</p>
+ *
+ * @return A string containing the system identifier, or null
+ * if none is available.
+ * @see org.xml.sax.Locator#getSystemId
+ */
+ public String getSystemId ()
+ {
+ return this.systemId;
+ }
+
+
+ /**
+ * The line number of the end of the text where the exception occurred.
+ *
+ * <p>The first line is line 1.</p>
+ *
+ * @return An integer representing the line number, or -1
+ * if none is available.
+ * @see org.xml.sax.Locator#getLineNumber
+ */
+ public int getLineNumber ()
+ {
+ return this.lineNumber;
+ }
+
+
+ /**
+ * The column number of the end of the text where the exception occurred.
+ *
+ * <p>The first column in a line is position 1.</p>
+ *
+ * @return An integer representing the column number, or -1
+ * if none is available.
+ * @see org.xml.sax.Locator#getColumnNumber
+ */
+ public int getColumnNumber ()
+ {
+ return this.columnNumber;
+ }
+
+ /**
+ * Override toString to provide more detailed error message.
+ *
+ * @return A string representation of this exception.
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder(getClass().getName());
+ String message = getLocalizedMessage();
+ if (publicId!=null) buf.append("publicId: ").append(publicId);
+ if (systemId!=null) buf.append("; systemId: ").append(systemId);
+ if (lineNumber!=-1) buf.append("; lineNumber: ").append(lineNumber);
+ if (columnNumber!=-1) buf.append("; columnNumber: ").append(columnNumber);
+
+ //append the exception message at the end
+ if (message!=null) buf.append("; ").append(message);
+ return buf.toString();
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ // Internal state.
+ //////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * @serial The public identifier, or null.
+ * @see #getPublicId
+ */
+ private String publicId;
+
+
+ /**
+ * @serial The system identifier, or null.
+ * @see #getSystemId
+ */
+ private String systemId;
+
+
+ /**
+ * @serial The line number, or -1.
+ * @see #getLineNumber
+ */
+ private int lineNumber;
+
+
+ /**
+ * @serial The column number, or -1.
+ * @see #getColumnNumber
+ */
+ private int columnNumber;
+
+ // Added serialVersionUID to preserve binary compatibility
+ static final long serialVersionUID = -5651165872476709336L;
+}
+
+// end of SAXParseException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/XMLReader.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2000, 2005, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// XMLReader.java - read an XML document.
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the Public Domain.
+// $Id: XMLReader.java,v 1.3 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+import java.io.IOException;
+
+
+/**
+ * Interface for reading an XML document using callbacks.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p><strong>Note:</strong> despite its name, this interface does
+ * <em>not</em> extend the standard Java {@link java.io.Reader Reader}
+ * interface, because reading XML is a fundamentally different activity
+ * than reading character data.</p>
+ *
+ * <p>XMLReader is the interface that an XML parser's SAX2 driver must
+ * implement. This interface allows an application to set and
+ * query features and properties in the parser, to register
+ * event handlers for document processing, and to initiate
+ * a document parse.</p>
+ *
+ * <p>All SAX interfaces are assumed to be synchronous: the
+ * {@link #parse parse} methods must not return until parsing
+ * is complete, and readers must wait for an event-handler callback
+ * to return before reporting the next event.</p>
+ *
+ * <p>This interface replaces the (now deprecated) SAX 1.0 {@link
+ * org.xml.sax.Parser Parser} interface. The XMLReader interface
+ * contains two important enhancements over the old Parser
+ * interface (as well as some minor ones):</p>
+ *
+ * <ol>
+ * <li>it adds a standard way to query and set features and
+ * properties; and</li>
+ * <li>it adds Namespace support, which is required for many
+ * higher-level XML standards.</li>
+ * </ol>
+ *
+ * <p>There are adapters available to convert a SAX1 Parser to
+ * a SAX2 XMLReader and vice-versa.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLFilter
+ * @see org.xml.sax.helpers.ParserAdapter
+ * @see org.xml.sax.helpers.XMLReaderAdapter
+ */
+public interface XMLReader
+{
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Configuration.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Look up the value of a feature flag.
+ *
+ * <p>The feature name is any fully-qualified URI. It is
+ * possible for an XMLReader to recognize a feature name but
+ * temporarily be unable to return its value.
+ * Some feature values may be available only in specific
+ * contexts, such as before, during, or after a parse.
+ * Also, some feature values may not be programmatically accessible.
+ * (In the case of an adapter for SAX1 {@link Parser}, there is no
+ * implementation-independent way to expose whether the underlying
+ * parser is performing validation, expanding external entities,
+ * and so forth.) </p>
+ *
+ * <p>All XMLReaders are required to recognize the
+ * http://xml.org/sax/features/namespaces and the
+ * http://xml.org/sax/features/namespace-prefixes feature names.</p>
+ *
+ * <p>Typical usage is something like this:</p>
+ *
+ * <pre>
+ * XMLReader r = new MySAXDriver();
+ *
+ * // try to activate validation
+ * try {
+ * r.setFeature("http://xml.org/sax/features/validation", true);
+ * } catch (SAXException e) {
+ * System.err.println("Cannot activate validation.");
+ * }
+ *
+ * // register event handlers
+ * r.setContentHandler(new MyContentHandler());
+ * r.setErrorHandler(new MyErrorHandler());
+ *
+ * // parse the first document
+ * try {
+ * r.parse("http://www.foo.com/mydoc.xml");
+ * } catch (IOException e) {
+ * System.err.println("I/O exception reading XML document");
+ * } catch (SAXException e) {
+ * System.err.println("XML exception reading document.");
+ * }
+ * </pre>
+ *
+ * <p>Implementors are free (and encouraged) to invent their own features,
+ * using names built on their own URIs.</p>
+ *
+ * @param name The feature name, which is a fully-qualified URI.
+ * @return The current value of the feature (true or false).
+ * @exception org.xml.sax.SAXNotRecognizedException If the feature
+ * value can't be assigned or retrieved.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the feature name but
+ * cannot determine its value at this time.
+ * @see #setFeature
+ */
+ public boolean getFeature (String name)
+ throws SAXNotRecognizedException, SAXNotSupportedException;
+
+
+ /**
+ * Set the value of a feature flag.
+ *
+ * <p>The feature name is any fully-qualified URI. It is
+ * possible for an XMLReader to expose a feature value but
+ * to be unable to change the current value.
+ * Some feature values may be immutable or mutable only
+ * in specific contexts, such as before, during, or after
+ * a parse.</p>
+ *
+ * <p>All XMLReaders are required to support setting
+ * http://xml.org/sax/features/namespaces to true and
+ * http://xml.org/sax/features/namespace-prefixes to false.</p>
+ *
+ * @param name The feature name, which is a fully-qualified URI.
+ * @param value The requested value of the feature (true or false).
+ * @exception org.xml.sax.SAXNotRecognizedException If the feature
+ * value can't be assigned or retrieved.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the feature name but
+ * cannot set the requested value.
+ * @see #getFeature
+ */
+ public void setFeature (String name, boolean value)
+ throws SAXNotRecognizedException, SAXNotSupportedException;
+
+
+ /**
+ * Look up the value of a property.
+ *
+ * <p>The property name is any fully-qualified URI. It is
+ * possible for an XMLReader to recognize a property name but
+ * temporarily be unable to return its value.
+ * Some property values may be available only in specific
+ * contexts, such as before, during, or after a parse.</p>
+ *
+ * <p>XMLReaders are not required to recognize any specific
+ * property names, though an initial core set is documented for
+ * SAX2.</p>
+ *
+ * <p>Implementors are free (and encouraged) to invent their own properties,
+ * using names built on their own URIs.</p>
+ *
+ * @param name The property name, which is a fully-qualified URI.
+ * @return The current value of the property.
+ * @exception org.xml.sax.SAXNotRecognizedException If the property
+ * value can't be assigned or retrieved.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the property name but
+ * cannot determine its value at this time.
+ * @see #setProperty
+ */
+ public Object getProperty (String name)
+ throws SAXNotRecognizedException, SAXNotSupportedException;
+
+
+ /**
+ * Set the value of a property.
+ *
+ * <p>The property name is any fully-qualified URI. It is
+ * possible for an XMLReader to recognize a property name but
+ * to be unable to change the current value.
+ * Some property values may be immutable or mutable only
+ * in specific contexts, such as before, during, or after
+ * a parse.</p>
+ *
+ * <p>XMLReaders are not required to recognize setting
+ * any specific property names, though a core set is defined by
+ * SAX2.</p>
+ *
+ * <p>This method is also the standard mechanism for setting
+ * extended handlers.</p>
+ *
+ * @param name The property name, which is a fully-qualified URI.
+ * @param value The requested value for the property.
+ * @exception org.xml.sax.SAXNotRecognizedException If the property
+ * value can't be assigned or retrieved.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the property name but
+ * cannot set the requested value.
+ */
+ public void setProperty (String name, Object value)
+ throws SAXNotRecognizedException, SAXNotSupportedException;
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Event handlers.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Allow an application to register an entity resolver.
+ *
+ * <p>If the application does not register an entity resolver,
+ * the XMLReader will perform its own default resolution.</p>
+ *
+ * <p>Applications may register a new or different resolver in the
+ * middle of a parse, and the SAX parser must begin using the new
+ * resolver immediately.</p>
+ *
+ * @param resolver The entity resolver.
+ * @see #getEntityResolver
+ */
+ public void setEntityResolver (EntityResolver resolver);
+
+
+ /**
+ * Return the current entity resolver.
+ *
+ * @return The current entity resolver, or null if none
+ * has been registered.
+ * @see #setEntityResolver
+ */
+ public EntityResolver getEntityResolver ();
+
+
+ /**
+ * Allow an application to register a DTD event handler.
+ *
+ * <p>If the application does not register a DTD handler, all DTD
+ * events reported by the SAX parser will be silently ignored.</p>
+ *
+ * <p>Applications may register a new or different handler in the
+ * middle of a parse, and the SAX parser must begin using the new
+ * handler immediately.</p>
+ *
+ * @param handler The DTD handler.
+ * @see #getDTDHandler
+ */
+ public void setDTDHandler (DTDHandler handler);
+
+
+ /**
+ * Return the current DTD handler.
+ *
+ * @return The current DTD handler, or null if none
+ * has been registered.
+ * @see #setDTDHandler
+ */
+ public DTDHandler getDTDHandler ();
+
+
+ /**
+ * Allow an application to register a content event handler.
+ *
+ * <p>If the application does not register a content handler, all
+ * content events reported by the SAX parser will be silently
+ * ignored.</p>
+ *
+ * <p>Applications may register a new or different handler in the
+ * middle of a parse, and the SAX parser must begin using the new
+ * handler immediately.</p>
+ *
+ * @param handler The content handler.
+ * @see #getContentHandler
+ */
+ public void setContentHandler (ContentHandler handler);
+
+
+ /**
+ * Return the current content handler.
+ *
+ * @return The current content handler, or null if none
+ * has been registered.
+ * @see #setContentHandler
+ */
+ public ContentHandler getContentHandler ();
+
+
+ /**
+ * Allow an application to register an error event handler.
+ *
+ * <p>If the application does not register an error handler, all
+ * error events reported by the SAX parser will be silently
+ * ignored; however, normal processing may not continue. It is
+ * highly recommended that all SAX applications implement an
+ * error handler to avoid unexpected bugs.</p>
+ *
+ * <p>Applications may register a new or different handler in the
+ * middle of a parse, and the SAX parser must begin using the new
+ * handler immediately.</p>
+ *
+ * @param handler The error handler.
+ * @see #getErrorHandler
+ */
+ public void setErrorHandler (ErrorHandler handler);
+
+
+ /**
+ * Return the current error handler.
+ *
+ * @return The current error handler, or null if none
+ * has been registered.
+ * @see #setErrorHandler
+ */
+ public ErrorHandler getErrorHandler ();
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Parsing.
+ ////////////////////////////////////////////////////////////////////
+
+ /**
+ * Parse an XML document.
+ *
+ * <p>The application can use this method to instruct the XML
+ * reader to begin parsing an XML document from any valid input
+ * source (a character stream, a byte stream, or a URI).</p>
+ *
+ * <p>Applications may not invoke this method while a parse is in
+ * progress (they should create a new XMLReader instead for each
+ * nested XML document). Once a parse is complete, an
+ * application may reuse the same XMLReader object, possibly with a
+ * different input source.
+ * Configuration of the XMLReader object (such as handler bindings and
+ * values established for feature flags and properties) is unchanged
+ * by completion of a parse, unless the definition of that aspect of
+ * the configuration explicitly specifies other behavior.
+ * (For example, feature flags or properties exposing
+ * characteristics of the document being parsed.)
+ * </p>
+ *
+ * <p>During the parse, the XMLReader will provide information
+ * about the XML document through the registered event
+ * handlers.</p>
+ *
+ * <p>This method is synchronous: it will not return until parsing
+ * has ended. If a client application wants to terminate
+ * parsing early, it should throw an exception.</p>
+ *
+ * @param input The input source for the top-level of the
+ * XML document.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @exception java.io.IOException An IO exception from the parser,
+ * possibly from a byte stream or character stream
+ * supplied by the application.
+ * @see org.xml.sax.InputSource
+ * @see #parse(java.lang.String)
+ * @see #setEntityResolver
+ * @see #setDTDHandler
+ * @see #setContentHandler
+ * @see #setErrorHandler
+ */
+ public void parse (InputSource input)
+ throws IOException, SAXException;
+
+
+ /**
+ * Parse an XML document from a system identifier (URI).
+ *
+ * <p>This method is a shortcut for the common case of reading a
+ * document from a system identifier. It is the exact
+ * equivalent of the following:</p>
+ *
+ * <pre>
+ * parse(new InputSource(systemId));
+ * </pre>
+ *
+ * <p>If the system identifier is a URL, it must be fully resolved
+ * by the application before it is passed to the parser.</p>
+ *
+ * @param systemId The system identifier (URI).
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @exception java.io.IOException An IO exception from the parser,
+ * possibly from a byte stream or character stream
+ * supplied by the application.
+ * @see #parse(org.xml.sax.InputSource)
+ */
+ public void parse (String systemId)
+ throws IOException, SAXException;
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/helpers/DefaultHandler.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2000, 2006, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// DefaultHandler.java - default implementation of the core handlers.
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the public domain.
+// $Id: DefaultHandler.java,v 1.3 2006/04/13 02:06:32 jeffsuttor Exp $
+
+package jdk.internal.org.xml.sax.helpers;
+
+import java.io.IOException;
+
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.Locator;
+import jdk.internal.org.xml.sax.Attributes;
+import jdk.internal.org.xml.sax.EntityResolver;
+import jdk.internal.org.xml.sax.DTDHandler;
+import jdk.internal.org.xml.sax.ContentHandler;
+import jdk.internal.org.xml.sax.ErrorHandler;
+import jdk.internal.org.xml.sax.SAXException;
+import jdk.internal.org.xml.sax.SAXParseException;
+
+
+/**
+ * Default base class for SAX2 event handlers.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This class is available as a convenience base class for SAX2
+ * applications: it provides default implementations for all of the
+ * callbacks in the four core SAX2 handler classes:</p>
+ *
+ * <ul>
+ * <li>{@link org.xml.sax.EntityResolver EntityResolver}</li>
+ * <li>{@link org.xml.sax.DTDHandler DTDHandler}</li>
+ * <li>{@link org.xml.sax.ContentHandler ContentHandler}</li>
+ * <li>{@link org.xml.sax.ErrorHandler ErrorHandler}</li>
+ * </ul>
+ *
+ * <p>Application writers can extend this class when they need to
+ * implement only part of an interface; parser writers can
+ * instantiate this class to provide default handlers when the
+ * application has not supplied its own.</p>
+ *
+ * <p>This class replaces the deprecated SAX1
+ * {@link org.xml.sax.HandlerBase HandlerBase} class.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson,
+ * @see org.xml.sax.EntityResolver
+ * @see org.xml.sax.DTDHandler
+ * @see org.xml.sax.ContentHandler
+ * @see org.xml.sax.ErrorHandler
+ */
+public class DefaultHandler
+ implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler
+{
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Default implementation of the EntityResolver interface.
+ ////////////////////////////////////////////////////////////////////
+
+ /**
+ * Resolve an external entity.
+ *
+ * <p>Always return null, so that the parser will use the system
+ * identifier provided in the XML document. This method implements
+ * the SAX default behaviour: application writers can override it
+ * in a subclass to do special translations such as catalog lookups
+ * or URI redirection.</p>
+ *
+ * @param publicId The public identifier, or null if none is
+ * available.
+ * @param systemId The system identifier provided in the XML
+ * document.
+ * @return The new input source, or null to require the
+ * default behaviour.
+ * @exception java.io.IOException If there is an error setting
+ * up the new input source.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.EntityResolver#resolveEntity
+ */
+ public InputSource resolveEntity (String publicId, String systemId)
+ throws IOException, SAXException
+ {
+ return null;
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Default implementation of DTDHandler interface.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Receive notification of a notation declaration.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass if they wish to keep track of the notations
+ * declared in a document.</p>
+ *
+ * @param name The notation name.
+ * @param publicId The notation public identifier, or null if not
+ * available.
+ * @param systemId The notation system identifier.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.DTDHandler#notationDecl
+ */
+ public void notationDecl (String name, String publicId, String systemId)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of an unparsed entity declaration.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to keep track of the unparsed entities
+ * declared in a document.</p>
+ *
+ * @param name The entity name.
+ * @param publicId The entity public identifier, or null if not
+ * available.
+ * @param systemId The entity system identifier.
+ * @param notationName The name of the associated notation.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+ */
+ public void unparsedEntityDecl (String name, String publicId,
+ String systemId, String notationName)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Default implementation of ContentHandler interface.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Receive a Locator object for document events.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass if they wish to store the locator for use
+ * with other document events.</p>
+ *
+ * @param locator A locator for all SAX document events.
+ * @see org.xml.sax.ContentHandler#setDocumentLocator
+ * @see org.xml.sax.Locator
+ */
+ public void setDocumentLocator (Locator locator)
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the beginning of the document.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the beginning
+ * of a document (such as allocating the root node of a tree or
+ * creating an output file).</p>
+ *
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#startDocument
+ */
+ public void startDocument ()
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the end of the document.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the end
+ * of a document (such as finalising a tree or closing an output
+ * file).</p>
+ *
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#endDocument
+ */
+ public void endDocument ()
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the start of a Namespace mapping.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the start of
+ * each Namespace prefix scope (such as storing the prefix mapping).</p>
+ *
+ * @param prefix The Namespace prefix being declared.
+ * @param uri The Namespace URI mapped to the prefix.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#startPrefixMapping
+ */
+ public void startPrefixMapping (String prefix, String uri)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the end of a Namespace mapping.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the end of
+ * each prefix mapping.</p>
+ *
+ * @param prefix The Namespace prefix being declared.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#endPrefixMapping
+ */
+ public void endPrefixMapping (String prefix)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the start of an element.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the start of
+ * each element (such as allocating a new tree node or writing
+ * output to a file).</p>
+ *
+ * @param uri The Namespace URI, or the empty string if the
+ * element has no Namespace URI or if Namespace
+ * processing is not being performed.
+ * @param localName The local name (without prefix), or the
+ * empty string if Namespace processing is not being
+ * performed.
+ * @param qName The qualified name (with prefix), or the
+ * empty string if qualified names are not available.
+ * @param attributes The attributes attached to the element. If
+ * there are no attributes, it shall be an empty
+ * Attributes object.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#startElement
+ */
+ public void startElement (String uri, String localName,
+ String qName, Attributes attributes)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the end of an element.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the end of
+ * each element (such as finalising a tree node or writing
+ * output to a file).</p>
+ *
+ * @param uri The Namespace URI, or the empty string if the
+ * element has no Namespace URI or if Namespace
+ * processing is not being performed.
+ * @param localName The local name (without prefix), or the
+ * empty string if Namespace processing is not being
+ * performed.
+ * @param qName The qualified name (with prefix), or the
+ * empty string if qualified names are not available.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#endElement
+ */
+ public void endElement (String uri, String localName, String qName)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of character data inside an element.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method to take specific actions for each chunk of character data
+ * (such as adding the data to a node or buffer, or printing it to
+ * a file).</p>
+ *
+ * @param ch The characters.
+ * @param start The start position in the character array.
+ * @param length The number of characters to use from the
+ * character array.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#characters
+ */
+ public void characters (char ch[], int start, int length)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of ignorable whitespace in element content.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method to take specific actions for each chunk of ignorable
+ * whitespace (such as adding data to a node or buffer, or printing
+ * it to a file).</p>
+ *
+ * @param ch The whitespace characters.
+ * @param start The start position in the character array.
+ * @param length The number of characters to use from the
+ * character array.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#ignorableWhitespace
+ */
+ public void ignorableWhitespace (char ch[], int start, int length)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of a processing instruction.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions for each
+ * processing instruction, such as setting status variables or
+ * invoking other methods.</p>
+ *
+ * @param target The processing instruction target.
+ * @param data The processing instruction data, or null if
+ * none is supplied.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#processingInstruction
+ */
+ public void processingInstruction (String target, String data)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of a skipped entity.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions for each
+ * processing instruction, such as setting status variables or
+ * invoking other methods.</p>
+ *
+ * @param name The name of the skipped entity.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#processingInstruction
+ */
+ public void skippedEntity (String name)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Default implementation of the ErrorHandler interface.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Receive notification of a parser warning.
+ *
+ * <p>The default implementation does nothing. Application writers
+ * may override this method in a subclass to take specific actions
+ * for each warning, such as inserting the message in a log file or
+ * printing it to the console.</p>
+ *
+ * @param e The warning information encoded as an exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ErrorHandler#warning
+ * @see org.xml.sax.SAXParseException
+ */
+ public void warning (SAXParseException e)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of a recoverable parser error.
+ *
+ * <p>The default implementation does nothing. Application writers
+ * may override this method in a subclass to take specific actions
+ * for each error, such as inserting the message in a log file or
+ * printing it to the console.</p>
+ *
+ * @param e The error information encoded as an exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ErrorHandler#warning
+ * @see org.xml.sax.SAXParseException
+ */
+ public void error (SAXParseException e)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Report a fatal XML parsing error.
+ *
+ * <p>The default implementation throws a SAXParseException.
+ * Application writers may override this method in a subclass if
+ * they need to take specific actions for each fatal error (such as
+ * collecting all of the errors into a single report): in any case,
+ * the application must stop all regular processing when this
+ * method is invoked, since the document is no longer reliable, and
+ * the parser may no longer report parsing events.</p>
+ *
+ * @param e The error information encoded as an exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ErrorHandler#fatalError
+ * @see org.xml.sax.SAXParseException
+ */
+ public void fatalError (SAXParseException e)
+ throws SAXException
+ {
+ throw e;
+ }
+
+}
+
+// end of DefaultHandler.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/BasicXmlPropertiesProvider.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package jdk.internal.util.xml;
+
+import java.util.Properties;
+import java.util.InvalidPropertiesFormatException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+import sun.util.spi.XmlPropertiesProvider;
+
+/**
+ * A {@code XmlPropertiesProvider} implementation that uses the UKit XML parser.
+ */
+
+public class BasicXmlPropertiesProvider extends XmlPropertiesProvider {
+
+ public BasicXmlPropertiesProvider() { }
+
+ @Override
+ public void load(Properties props, InputStream in)
+ throws IOException, InvalidPropertiesFormatException
+ {
+ PropertiesDefaultHandler handler = new PropertiesDefaultHandler();
+ handler.load(props, in);
+ }
+
+ @Override
+ public void store(Properties props, OutputStream os, String comment,
+ String encoding)
+ throws IOException
+ {
+ PropertiesDefaultHandler handler = new PropertiesDefaultHandler();
+ handler.store(props, os, comment, encoding);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/PropertiesDefaultHandler.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml;
+
+import java.io.*;
+import java.util.InvalidPropertiesFormatException;
+import java.util.Properties;
+import jdk.internal.org.xml.sax.Attributes;
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.SAXException;
+import jdk.internal.org.xml.sax.SAXParseException;
+import jdk.internal.org.xml.sax.helpers.DefaultHandler;
+import jdk.internal.util.xml.impl.SAXParserImpl;
+import jdk.internal.util.xml.impl.XMLStreamWriterImpl;
+
+/**
+ * A class used to aid in Properties load and save in XML. This class is
+ * re-implemented using a subset of SAX
+ *
+ * @author Joe Wang
+ * @since 8
+ */
+public class PropertiesDefaultHandler extends DefaultHandler {
+
+ // Elements specified in the properties.dtd
+ private static final String ELEMENT_ROOT = "properties";
+ private static final String ELEMENT_COMMENT = "comment";
+ private static final String ELEMENT_ENTRY = "entry";
+ private static final String ATTR_KEY = "key";
+ // The required DTD URI for exported properties
+ private static final String PROPS_DTD_DECL =
+ "<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">";
+ private static final String PROPS_DTD_URI =
+ "http://java.sun.com/dtd/properties.dtd";
+ private static final String PROPS_DTD =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<!-- DTD for properties -->"
+ + "<!ELEMENT properties ( comment?, entry* ) >"
+ + "<!ATTLIST properties"
+ + " version CDATA #FIXED \"1.0\">"
+ + "<!ELEMENT comment (#PCDATA) >"
+ + "<!ELEMENT entry (#PCDATA) >"
+ + "<!ATTLIST entry "
+ + " key CDATA #REQUIRED>";
+ /**
+ * Version number for the format of exported properties files.
+ */
+ private static final String EXTERNAL_XML_VERSION = "1.0";
+ private Properties properties;
+
+ public void load(Properties props, InputStream in)
+ throws IOException, InvalidPropertiesFormatException, UnsupportedEncodingException
+ {
+ this.properties = props;
+
+ try {
+ SAXParser parser = new SAXParserImpl();
+ parser.parse(in, this);
+ } catch (SAXException saxe) {
+ throw new InvalidPropertiesFormatException(saxe);
+ }
+
+ /**
+ * String xmlVersion = propertiesElement.getAttribute("version"); if
+ * (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0) throw new
+ * InvalidPropertiesFormatException( "Exported Properties file format
+ * version " + xmlVersion + " is not supported. This java installation
+ * can read" + " versions " + EXTERNAL_XML_VERSION + " or older. You" +
+ * " may need to install a newer version of JDK.");
+ */
+ }
+
+ public void store(Properties props, OutputStream os, String comment, String encoding)
+ throws IOException
+ {
+ try {
+ XMLStreamWriter writer = new XMLStreamWriterImpl(os, encoding);
+ writer.writeStartDocument();
+ writer.writeDTD(PROPS_DTD_DECL);
+ writer.writeStartElement(ELEMENT_ROOT);
+ if (comment != null && comment.length() > 0) {
+ writer.writeStartElement(ELEMENT_COMMENT);
+ writer.writeCharacters(comment);
+ writer.writeEndElement();
+ }
+
+ for (String key : props.stringPropertyNames()) {
+ String val = props.getProperty(key);
+ writer.writeStartElement(ELEMENT_ENTRY);
+ writer.writeAttribute(ATTR_KEY, key);
+ writer.writeCharacters(val);
+ writer.writeEndElement();
+ }
+
+ writer.writeEndElement();
+ writer.writeEndDocument();
+ writer.close();
+ } catch (XMLStreamException e) {
+ if (e.getCause() instanceof UnsupportedEncodingException) {
+ throw (UnsupportedEncodingException) e.getCause();
+ }
+ throw new IOException(e);
+ }
+
+ }
+ ////////////////////////////////////////////////////////////////////
+ // Validate while parsing
+ ////////////////////////////////////////////////////////////////////
+ final static String ALLOWED_ELEMENTS = "properties, comment, entry";
+ final static String ALLOWED_COMMENT = "comment";
+ ////////////////////////////////////////////////////////////////////
+ // Handler methods
+ ////////////////////////////////////////////////////////////////////
+ StringBuffer buf = new StringBuffer();
+ boolean sawComment = false;
+ boolean validEntry = false;
+ int rootElem = 0;
+ String key;
+ String rootElm;
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException
+ {
+ if (rootElem < 2) {
+ rootElem++;
+ }
+
+ if (rootElm == null) {
+ fatalError(new SAXParseException("An XML properties document must contain"
+ + " the DOCTYPE declaration as defined by java.util.Properties.", null));
+ }
+
+ if (rootElem == 1 && !rootElm.equals(qName)) {
+ fatalError(new SAXParseException("Document root element \"" + qName
+ + "\", must match DOCTYPE root \"" + rootElm + "\"", null));
+ }
+ if (!ALLOWED_ELEMENTS.contains(qName)) {
+ fatalError(new SAXParseException("Element type \"" + qName + "\" must be declared.", null));
+ }
+ if (qName.equals(ELEMENT_ENTRY)) {
+ validEntry = true;
+ key = attributes.getValue(ATTR_KEY);
+ if (key == null) {
+ fatalError(new SAXParseException("Attribute \"key\" is required and must be specified for element type \"entry\"", null));
+ }
+ } else if (qName.equals(ALLOWED_COMMENT)) {
+ if (sawComment) {
+ fatalError(new SAXParseException("Only one comment element may be allowed. "
+ + "The content of element type \"properties\" must match \"(comment?,entry*)\"", null));
+ }
+ sawComment = true;
+ }
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ if (validEntry) {
+ buf.append(ch, start, length);
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (!ALLOWED_ELEMENTS.contains(qName)) {
+ fatalError(new SAXParseException("Element: " + qName + " is invalid, must match \"(comment?,entry*)\".", null));
+ }
+
+ if (validEntry) {
+ properties.setProperty(key, buf.toString());
+ buf.delete(0, buf.length());
+ validEntry = false;
+ }
+ }
+
+ @Override
+ public void notationDecl(String name, String publicId, String systemId) throws SAXException {
+ rootElm = name;
+ }
+
+ @Override
+ public InputSource resolveEntity(String pubid, String sysid)
+ throws SAXException, IOException {
+ {
+ if (sysid.equals(PROPS_DTD_URI)) {
+ InputSource is;
+ is = new InputSource(new StringReader(PROPS_DTD));
+ is.setSystemId(PROPS_DTD_URI);
+ return is;
+ }
+ throw new SAXException("Invalid system identifier: " + sysid);
+ }
+ }
+
+ @Override
+ public void error(SAXParseException x) throws SAXException {
+ throw x;
+ }
+
+ @Override
+ public void fatalError(SAXParseException x) throws SAXException {
+ throw x;
+ }
+
+ @Override
+ public void warning(SAXParseException x) throws SAXException {
+ throw x;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/SAXParser.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.SAXException;
+import jdk.internal.org.xml.sax.XMLReader;
+import jdk.internal.org.xml.sax.helpers.DefaultHandler;
+
+
+/**
+ * Defines the API that wraps an {@link org.xml.sax.XMLReader}
+ * implementation class. In JAXP 1.0, this class wrapped the
+ * {@link org.xml.sax.Parser} interface, however this interface was
+ * replaced by the {@link org.xml.sax.XMLReader}. For ease
+ * of transition, this class continues to support the same name
+ * and interface as well as supporting new methods.
+ *
+ * An instance of this class can be obtained from the
+ * {@link javax.xml.parsers.SAXParserFactory#newSAXParser()} method.
+ * Once an instance of this class is obtained, XML can be parsed from
+ * a variety of input sources. These input sources are InputStreams,
+ * Files, URLs, and SAX InputSources.<p>
+ *
+ * This static method creates a new factory instance based
+ * on a system property setting or uses the platform default
+ * if no property has been defined.<p>
+ *
+ * The system property that controls which Factory implementation
+ * to create is named <code>"javax.xml.parsers.SAXParserFactory"</code>.
+ * This property names a class that is a concrete subclass of this
+ * abstract class. If no property is defined, a platform default
+ * will be used.</p>
+ *
+ * As the content is parsed by the underlying parser, methods of the
+ * given
+ * {@link org.xml.sax.helpers.DefaultHandler} are called.<p>
+ *
+ * Implementors of this class which wrap an underlaying implementation
+ * can consider using the {@link org.xml.sax.helpers.ParserAdapter}
+ * class to initially adapt their SAX1 implementation to work under
+ * this revised class.
+ *
+ * @author <a href="mailto:Jeff.Suttor@Sun.com">Jeff Suttor</a>
+ * @version $Revision: 1.8 $, $Date: 2010-11-01 04:36:09 $
+ *
+ * @author Joe Wang
+ * This is a subset of that in JAXP, javax.xml.parsers.SAXParser
+ *
+ */
+public abstract class SAXParser {
+
+ /**
+ * <p>Protected constructor to prevent instantiation.</p>
+ */
+ protected SAXParser() {
+ }
+
+ /**
+ * Parse the content of the given {@link java.io.InputStream}
+ * instance as XML using the specified
+ * {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param is InputStream containing the content to be parsed.
+ * @param dh The SAX DefaultHandler to use.
+ *
+ * @throws IllegalArgumentException If the given InputStream is null.
+ * @throws IOException If any IO errors occur.
+ * @throws SAXException If any SAX errors occur during processing.
+ *
+ * @see org.xml.sax.DocumentHandler
+ */
+ public void parse(InputStream is, DefaultHandler dh)
+ throws SAXException, IOException
+ {
+ if (is == null) {
+ throw new IllegalArgumentException("InputStream cannot be null");
+ }
+
+ InputSource input = new InputSource(is);
+ this.parse(input, dh);
+ }
+
+ /**
+ * Parse the content described by the giving Uniform Resource
+ * Identifier (URI) as XML using the specified
+ * {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param uri The location of the content to be parsed.
+ * @param dh The SAX DefaultHandler to use.
+ *
+ * @throws IllegalArgumentException If the uri is null.
+ * @throws IOException If any IO errors occur.
+ * @throws SAXException If any SAX errors occur during processing.
+ *
+ * @see org.xml.sax.DocumentHandler
+ */
+ public void parse(String uri, DefaultHandler dh)
+ throws SAXException, IOException
+ {
+ if (uri == null) {
+ throw new IllegalArgumentException("uri cannot be null");
+ }
+
+ InputSource input = new InputSource(uri);
+ this.parse(input, dh);
+ }
+
+ /**
+ * Parse the content of the file specified as XML using the
+ * specified {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param f The file containing the XML to parse
+ * @param dh The SAX DefaultHandler to use.
+ *
+ * @throws IllegalArgumentException If the File object is null.
+ * @throws IOException If any IO errors occur.
+ * @throws SAXException If any SAX errors occur during processing.
+ *
+ * @see org.xml.sax.DocumentHandler
+ */
+ public void parse(File f, DefaultHandler dh)
+ throws SAXException, IOException
+ {
+ if (f == null) {
+ throw new IllegalArgumentException("File cannot be null");
+ }
+
+ //convert file to appropriate URI, f.toURI().toASCIIString()
+ //converts the URI to string as per rule specified in
+ //RFC 2396,
+ InputSource input = new InputSource(f.toURI().toASCIIString());
+ this.parse(input, dh);
+ }
+
+ /**
+ * Parse the content given {@link org.xml.sax.InputSource}
+ * as XML using the specified
+ * {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param is The InputSource containing the content to be parsed.
+ * @param dh The SAX DefaultHandler to use.
+ *
+ * @throws IllegalArgumentException If the <code>InputSource</code> object
+ * is <code>null</code>.
+ * @throws IOException If any IO errors occur.
+ * @throws SAXException If any SAX errors occur during processing.
+ *
+ * @see org.xml.sax.DocumentHandler
+ */
+ public void parse(InputSource is, DefaultHandler dh)
+ throws SAXException, IOException
+ {
+ if (is == null) {
+ throw new IllegalArgumentException("InputSource cannot be null");
+ }
+
+ XMLReader reader = this.getXMLReader();
+ if (dh != null) {
+ reader.setContentHandler(dh);
+ reader.setEntityResolver(dh);
+ reader.setErrorHandler(dh);
+ reader.setDTDHandler(dh);
+ }
+ reader.parse(is);
+ }
+
+ /**
+ * Returns the {@link org.xml.sax.XMLReader} that is encapsulated by the
+ * implementation of this class.
+ *
+ * @return The XMLReader that is encapsulated by the
+ * implementation of this class.
+ *
+ * @throws SAXException If any SAX errors occur during processing.
+ */
+ public abstract XMLReader getXMLReader() throws SAXException;
+
+ /**
+ * Indicates whether or not this parser is configured to
+ * understand namespaces.
+ *
+ * @return true if this parser is configured to
+ * understand namespaces; false otherwise.
+ */
+ public abstract boolean isNamespaceAware();
+
+ /**
+ * Indicates whether or not this parser is configured to
+ * validate XML documents.
+ *
+ * @return true if this parser is configured to
+ * validate XML documents; false otherwise.
+ */
+ public abstract boolean isValidating();
+
+ /**
+ * <p>Get the XInclude processing mode for this parser.</p>
+ *
+ * @return
+ * the return value of
+ * the {@link SAXParserFactory#isXIncludeAware()}
+ * when this parser was created from factory.
+ *
+ * @throws UnsupportedOperationException When implementation does not
+ * override this method
+ *
+ * @since 1.5
+ *
+ * @see SAXParserFactory#setXIncludeAware(boolean)
+ */
+ public boolean isXIncludeAware() {
+ throw new UnsupportedOperationException(
+ "This parser does not support specification \""
+ + this.getClass().getPackage().getSpecificationTitle()
+ + "\" version \""
+ + this.getClass().getPackage().getSpecificationVersion()
+ + "\"");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/XMLStreamException.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml;
+
+/**
+ * A copy of the StAX XMLStreamException without Location support
+ *
+ * The base exception for unexpected processing errors. This Exception
+ * class is used to report well-formedness errors as well as unexpected
+ * processing conditions.
+ * @version 1.0
+ * @author Copyright (c) 2009 by Oracle Corporation. All Rights Reserved.
+ * @since 1.6
+ */
+
+public class XMLStreamException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+
+ protected Throwable nested;
+
+ /**
+ * Default constructor
+ */
+ public XMLStreamException() {
+ super();
+ }
+
+ /**
+ * Construct an exception with the assocated message.
+ *
+ * @param msg the message to report
+ */
+ public XMLStreamException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Construct an exception with the assocated exception
+ *
+ * @param th a nested exception
+ */
+ public XMLStreamException(Throwable th) {
+ super(th);
+ nested = th;
+ }
+
+ /**
+ * Construct an exception with the assocated message and exception
+ *
+ * @param th a nested exception
+ * @param msg the message to report
+ */
+ public XMLStreamException(String msg, Throwable th) {
+ super(msg, th);
+ nested = th;
+ }
+
+ /**
+ * Gets the nested exception.
+ *
+ * @return Nested exception
+ */
+ public Throwable getNestedException() {
+ return nested;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/XMLStreamWriter.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml;
+
+/**
+ * Basic XMLStreamWriter for writing simple XML files such as those
+ * defined in java.util.Properties
+ *
+ * This is a subset of javax.xml.stream.XMLStreamWriter
+ *
+ * @author Joe Wang
+ */
+public interface XMLStreamWriter {
+
+ //Defaults the XML version to 1.0, and the encoding to utf-8
+ public final static String DEFAULT_XML_VERSION = "1.0";
+ public final static String DEFAULT_ENCODING = "UTF-8";
+
+ /**
+ * Writes a start tag to the output. All writeStartElement methods
+ * open a new scope in the internal namespace context. Writing the
+ * corresponding EndElement causes the scope to be closed.
+ * @param localName local name of the tag, may not be null
+ * @throws XMLStreamException
+ */
+ public void writeStartElement(String localName) throws XMLStreamException;
+
+ /**
+ * Writes an empty element tag to the output
+ * @param localName local name of the tag, may not be null
+ * @throws XMLStreamException
+ */
+ public void writeEmptyElement(String localName) throws XMLStreamException;
+
+ /**
+ * Writes an end tag to the output relying on the internal
+ * state of the writer to determine the prefix and local name
+ * of the event.
+ * @throws XMLStreamException
+ */
+ public void writeEndElement() throws XMLStreamException;
+
+ /**
+ * Closes any start tags and writes corresponding end tags.
+ * @throws XMLStreamException
+ */
+ public void writeEndDocument() throws XMLStreamException;
+
+ /**
+ * Close this writer and free any resources associated with the
+ * writer. This must not close the underlying output stream.
+ * @throws XMLStreamException
+ */
+ public void close() throws XMLStreamException;
+
+ /**
+ * Write any cached data to the underlying output mechanism.
+ * @throws XMLStreamException
+ */
+ public void flush() throws XMLStreamException;
+
+ /**
+ * Writes an attribute to the output stream without
+ * a prefix.
+ * @param localName the local name of the attribute
+ * @param value the value of the attribute
+ * @throws IllegalStateException if the current state does not allow Attribute writing
+ * @throws XMLStreamException
+ */
+ public void writeAttribute(String localName, String value)
+ throws XMLStreamException;
+
+ /**
+ * Writes a CData section
+ * @param data the data contained in the CData Section, may not be null
+ * @throws XMLStreamException
+ */
+ public void writeCData(String data) throws XMLStreamException;
+
+ /**
+ * Write a DTD section. This string represents the entire doctypedecl production
+ * from the XML 1.0 specification.
+ *
+ * @param dtd the DTD to be written
+ * @throws XMLStreamException
+ */
+ public void writeDTD(String dtd) throws XMLStreamException;
+
+ /**
+ * Write the XML Declaration. Defaults the XML version to 1.0, and the encoding to utf-8
+ * @throws XMLStreamException
+ */
+ public void writeStartDocument() throws XMLStreamException;
+
+ /**
+ * Write the XML Declaration. Defaults the the encoding to utf-8
+ * @param version version of the xml document
+ * @throws XMLStreamException
+ */
+ public void writeStartDocument(String version) throws XMLStreamException;
+
+ /**
+ * Write the XML Declaration. Note that the encoding parameter does
+ * not set the actual encoding of the underlying output. That must
+ * be set when the instance of the XMLStreamWriter is created using the
+ * XMLOutputFactory
+ * @param encoding encoding of the xml declaration
+ * @param version version of the xml document
+ * @throws XMLStreamException If given encoding does not match encoding
+ * of the underlying stream
+ */
+ public void writeStartDocument(String encoding, String version)
+ throws XMLStreamException;
+
+ /**
+ * Write text to the output
+ * @param text the value to write
+ * @throws XMLStreamException
+ */
+ public void writeCharacters(String text) throws XMLStreamException;
+
+ /**
+ * Write text to the output
+ * @param text the value to write
+ * @param start the starting position in the array
+ * @param len the number of characters to write
+ * @throws XMLStreamException
+ */
+ public void writeCharacters(char[] text, int start, int len)
+ throws XMLStreamException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/Attrs.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import jdk.internal.org.xml.sax.Attributes;
+
+public class Attrs implements Attributes {
+
+ /**
+ * Attributes string array. Each individual attribute is represented by four
+ * strings: namespace URL(+0), qname(+1), local name(+2), value(+3),
+ * type(+4), declared["d"] and default["D"](+5). In order to find attribute
+ * by the attrubute index, the attribute index MUST be multiplied by 8. The
+ * result will point to the attribute namespace URL.
+ */
+ /* pkg */ String[] mItems;
+ /**
+ * Number of attributes in the attributes string array.
+ */
+ private char mLength;
+ /**
+ * current index
+ */
+ private char mAttrIdx = 0;
+
+ /**
+ * Constructor.
+ */
+ public Attrs() {
+ // The default number of attributies capacity is 8.
+ mItems = new String[(8 << 3)];
+ }
+
+ /**
+ * Sets up the number of attributes and ensure the capacity of the attribute
+ * string array.
+ *
+ * @param length The number of attributes in the object.
+ */
+ public void setLength(char length) {
+ if (length > ((char) (mItems.length >> 3))) {
+ mItems = new String[length << 3];
+ }
+ mLength = length;
+ }
+
+ /**
+ * Return the number of attributes in the list.
+ *
+ * <p>Once you know the number of attributes, you can iterate through the
+ * list.</p>
+ *
+ * @return The number of attributes in the list.
+ * @see #getURI(int)
+ * @see #getLocalName(int)
+ * @see #getQName(int)
+ * @see #getType(int)
+ * @see #getValue(int)
+ */
+ public int getLength() {
+ return mLength;
+ }
+
+ /**
+ * Look up an attribute's Namespace URI by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The Namespace URI, or the empty string if none is available, or
+ * null if the index is out of range.
+ * @see #getLength
+ */
+ public String getURI(int index) {
+ return ((index >= 0) && (index < mLength))
+ ? (mItems[index << 3])
+ : null;
+ }
+
+ /**
+ * Look up an attribute's local name by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The local name, or the empty string if Namespace processing is
+ * not being performed, or null if the index is out of range.
+ * @see #getLength
+ */
+ public String getLocalName(int index) {
+ return ((index >= 0) && (index < mLength))
+ ? (mItems[(index << 3) + 2])
+ : null;
+ }
+
+ /**
+ * Look up an attribute's XML 1.0 qualified name by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The XML 1.0 qualified name, or the empty string if none is
+ * available, or null if the index is out of range.
+ * @see #getLength
+ */
+ public String getQName(int index) {
+ if ((index < 0) || (index >= mLength)) {
+ return null;
+ }
+ return mItems[(index << 3) + 1];
+ }
+
+ /**
+ * Look up an attribute's type by index.
+ *
+ * <p>The attribute type is one of the strings "CDATA", "ID", "IDREF",
+ * "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES", or "NOTATION"
+ * (always in upper case).</p>
+ *
+ * <p>If the parser has not read a declaration for the attribute, or if the
+ * parser does not report attribute types, then it must return the value
+ * "CDATA" as stated in the XML 1.0 Recommentation (clause 3.3.3,
+ * "Attribute-Value Normalization").</p>
+ *
+ * <p>For an enumerated attribute that is not a notation, the parser will
+ * report the type as "NMTOKEN".</p>
+ *
+ * @param index The attribute index (zero-based).
+ * @return The attribute's type as a string, or null if the index is out of
+ * range.
+ * @see #getLength
+ */
+ public String getType(int index) {
+ return ((index >= 0) && (index < (mItems.length >> 3)))
+ ? (mItems[(index << 3) + 4])
+ : null;
+ }
+
+ /**
+ * Look up an attribute's value by index.
+ *
+ * <p>If the attribute value is a list of tokens (IDREFS, ENTITIES, or
+ * NMTOKENS), the tokens will be concatenated into a single string with each
+ * token separated by a single space.</p>
+ *
+ * @param index The attribute index (zero-based).
+ * @return The attribute's value as a string, or null if the index is out of
+ * range.
+ * @see #getLength
+ */
+ public String getValue(int index) {
+ return ((index >= 0) && (index < mLength))
+ ? (mItems[(index << 3) + 3])
+ : null;
+ }
+
+ /**
+ * Look up the index of an attribute by Namespace name.
+ *
+ * @param uri The Namespace URI, or the empty string if the name has no
+ * Namespace URI.
+ * @param localName The attribute's local name.
+ * @return The index of the attribute, or -1 if it does not appear in the
+ * list.
+ */
+ public int getIndex(String uri, String localName) {
+ char len = mLength;
+ for (char idx = 0; idx < len; idx++) {
+ if ((mItems[idx << 3]).equals(uri)
+ && mItems[(idx << 3) + 2].equals(localName)) {
+ return idx;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Look up the index of an attribute by Namespace name.
+ *
+ * @param uri The Namespace URI, or the empty string if the name has no
+ * Namespace URI. <code>null</code> value enforce the search by the local
+ * name only.
+ * @param localName The attribute's local name.
+ * @return The index of the attribute, or -1 if it does not appear in the
+ * list.
+ */
+ /* pkg */ int getIndexNullNS(String uri, String localName) {
+ char len = mLength;
+ if (uri != null) {
+ for (char idx = 0; idx < len; idx++) {
+ if ((mItems[idx << 3]).equals(uri)
+ && mItems[(idx << 3) + 2].equals(localName)) {
+ return idx;
+ }
+ }
+ } else {
+ for (char idx = 0; idx < len; idx++) {
+ if (mItems[(idx << 3) + 2].equals(localName)) {
+ return idx;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Look up the index of an attribute by XML 1.0 qualified name.
+ *
+ * @param qName The qualified (prefixed) name.
+ * @return The index of the attribute, or -1 if it does not appear in the
+ * list.
+ */
+ public int getIndex(String qName) {
+ char len = mLength;
+ for (char idx = 0; idx < len; idx++) {
+ if (mItems[(idx << 3) + 1].equals(qName)) {
+ return idx;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Look up an attribute's type by Namespace name.
+ *
+ * <p>See {@link #getType(int) getType(int)} for a description of the
+ * possible types.</p>
+ *
+ * @param uri The Namespace URI, or the empty String if the name has no
+ * Namespace URI.
+ * @param localName The local name of the attribute.
+ * @return The attribute type as a string, or null if the attribute is not
+ * in the list or if Namespace processing is not being performed.
+ */
+ public String getType(String uri, String localName) {
+ int idx = getIndex(uri, localName);
+ return (idx >= 0) ? (mItems[(idx << 3) + 4]) : null;
+ }
+
+ /**
+ * Look up an attribute's type by XML 1.0 qualified name.
+ *
+ * <p>See {@link #getType(int) getType(int)} for a description of the
+ * possible types.</p>
+ *
+ * @param qName The XML 1.0 qualified name.
+ * @return The attribute type as a string, or null if the attribute is not
+ * in the list or if qualified names are not available.
+ */
+ public String getType(String qName) {
+ int idx = getIndex(qName);
+ return (idx >= 0) ? (mItems[(idx << 3) + 4]) : null;
+ }
+
+ /**
+ * Look up an attribute's value by Namespace name.
+ *
+ * <p>See {@link #getValue(int) getValue(int)} for a description of the
+ * possible values.</p>
+ *
+ * @param uri The Namespace URI, or the empty String if the name has no
+ * Namespace URI.
+ * @param localName The local name of the attribute.
+ * @return The attribute value as a string, or null if the attribute is not
+ * in the list.
+ */
+ public String getValue(String uri, String localName) {
+ int idx = getIndex(uri, localName);
+ return (idx >= 0) ? (mItems[(idx << 3) + 3]) : null;
+ }
+
+ /**
+ * Look up an attribute's value by XML 1.0 qualified name.
+ *
+ * <p>See {@link #getValue(int) getValue(int)} for a description of the
+ * possible values.</p>
+ *
+ * @param qName The XML 1.0 qualified name.
+ * @return The attribute value as a string, or null if the attribute is not
+ * in the list or if qualified names are not available.
+ */
+ public String getValue(String qName) {
+ int idx = getIndex(qName);
+ return (idx >= 0) ? (mItems[(idx << 3) + 3]) : null;
+ }
+
+ /**
+ * Returns false unless the attribute was declared in the DTD. This helps
+ * distinguish two kinds of attributes that SAX reports as CDATA: ones that
+ * were declared (and hence are usually valid), and those that were not (and
+ * which are never valid).
+ *
+ * @param index The attribute index (zero-based).
+ * @return true if the attribute was declared in the DTD, false otherwise.
+ * @exception java.lang.ArrayIndexOutOfBoundsException When the supplied
+ * index does not identify an attribute.
+ */
+ public boolean isDeclared(int index) {
+ if ((index < 0) || (index >= mLength)) {
+ throw new ArrayIndexOutOfBoundsException("");
+ }
+
+ return ((mItems[(index << 3) + 5]) != null);
+ }
+
+ /**
+ * Returns false unless the attribute was declared in the DTD. This helps
+ * distinguish two kinds of attributes that SAX reports as CDATA: ones that
+ * were declared (and hence are usually valid), and those that were not (and
+ * which are never valid).
+ *
+ * @param qName The XML qualified (prefixed) name.
+ * @return true if the attribute was declared in the DTD, false otherwise.
+ * @exception java.lang.IllegalArgumentException When the supplied name does
+ * not identify an attribute.
+ */
+ public boolean isDeclared(String qName) {
+ int idx = getIndex(qName);
+ if (idx < 0) {
+ throw new IllegalArgumentException("");
+ }
+
+ return ((mItems[(idx << 3) + 5]) != null);
+ }
+
+ /**
+ * Returns false unless the attribute was declared in the DTD. This helps
+ * distinguish two kinds of attributes that SAX reports as CDATA: ones that
+ * were declared (and hence are usually valid), and those that were not (and
+ * which are never valid).
+ *
+ * <p>Remember that since DTDs do not "understand" namespaces, the namespace
+ * URI associated with an attribute may not have come from the DTD. The
+ * declaration will have applied to the attribute's <em>qName</em>.
+ *
+ * @param uri The Namespace URI, or the empty string if the name has no
+ * Namespace URI.
+ * @param localName The attribute's local name.
+ * @return true if the attribute was declared in the DTD, false otherwise.
+ * @exception java.lang.IllegalArgumentException When the supplied names do
+ * not identify an attribute.
+ */
+ public boolean isDeclared(String uri, String localName) {
+ int idx = getIndex(uri, localName);
+ if (idx < 0) {
+ throw new IllegalArgumentException("");
+ }
+
+ return ((mItems[(idx << 3) + 5]) != null);
+ }
+
+ /**
+ * Returns true unless the attribute value was provided by DTD defaulting.
+ *
+ * @param index The attribute index (zero-based).
+ * @return true if the value was found in the XML text, false if the value
+ * was provided by DTD defaulting.
+ * @exception java.lang.ArrayIndexOutOfBoundsException When the supplied
+ * index does not identify an attribute.
+ */
+ public boolean isSpecified(int index) {
+ if ((index < 0) || (index >= mLength)) {
+ throw new ArrayIndexOutOfBoundsException("");
+ }
+
+ String str = mItems[(index << 3) + 5];
+ return ((str != null) ? (str.charAt(0) == 'd') : true);
+ }
+
+ /**
+ * Returns true unless the attribute value was provided by DTD defaulting.
+ *
+ * <p>Remember that since DTDs do not "understand" namespaces, the namespace
+ * URI associated with an attribute may not have come from the DTD. The
+ * declaration will have applied to the attribute's <em>qName</em>.
+ *
+ * @param uri The Namespace URI, or the empty string if the name has no
+ * Namespace URI.
+ * @param localName The attribute's local name.
+ * @return true if the value was found in the XML text, false if the value
+ * was provided by DTD defaulting.
+ * @exception java.lang.IllegalArgumentException When the supplied names do
+ * not identify an attribute.
+ */
+ public boolean isSpecified(String uri, String localName) {
+ int idx = getIndex(uri, localName);
+ if (idx < 0) {
+ throw new IllegalArgumentException("");
+ }
+
+ String str = mItems[(idx << 3) + 5];
+ return ((str != null) ? (str.charAt(0) == 'd') : true);
+ }
+
+ /**
+ * Returns true unless the attribute value was provided by DTD defaulting.
+ *
+ * @param qName The XML qualified (prefixed) name.
+ * @return true if the value was found in the XML text, false if the value
+ * was provided by DTD defaulting.
+ * @exception java.lang.IllegalArgumentException When the supplied name does
+ * not identify an attribute.
+ */
+ public boolean isSpecified(String qName) {
+ int idx = getIndex(qName);
+ if (idx < 0) {
+ throw new IllegalArgumentException("");
+ }
+
+ String str = mItems[(idx << 3) + 5];
+ return ((str != null) ? (str.charAt(0) == 'd') : true);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/Input.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.Reader;
+
+/**
+ * A parsed entity input state.
+ *
+ * This class represents a parsed entity input state. The parser uses
+ * an instance of this class to manage input.
+ */
+
+public class Input {
+
+ /** The entity public identifier or null. */
+ public String pubid;
+ /** The entity systen identifier or null. */
+ public String sysid;
+ /** The encoding from XML declaration or null */
+ public String xmlenc;
+ /** The XML version from XML declaration or 0x0000 */
+ public char xmlver;
+ /** The entity reader. */
+ public Reader src;
+ /** The character buffer. */
+ public char[] chars;
+ /** The length of the character buffer. */
+ public int chLen;
+ /** The index of the next character to read. */
+ public int chIdx;
+ /** The next input in a chain. */
+ public Input next;
+
+ /**
+ * Constructor.
+ *
+ * @param buffsize The input buffer size.
+ */
+ public Input(int buffsize) {
+ chars = new char[buffsize];
+ chLen = chars.length;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param buff The input buffer.
+ */
+ public Input(char[] buff) {
+ chars = buff;
+ chLen = chars.length;
+ }
+
+ /**
+ * Constructor.
+ */
+ public Input() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/Pair.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+
+/**
+ * A name with value pair.
+ *
+ * This class keeps name with value pair with additional information and
+ * supports pair chaining.
+ */
+public class Pair {
+
+ /** The pair name. */
+ public String name;
+ /** The pair value. */
+ public String value;
+ /** The pair numeric value. */
+ public int num;
+ /** The characters of name. */
+ public char[] chars;
+ /** The pair identifier. */
+ public int id;
+ /** The list of associated pairs. */
+ public Pair list;
+ /** The next pair in a chain. */
+ public Pair next;
+
+ /**
+ * Creates a qualified name string from qualified name.
+ *
+ * @return The qualified name string.
+ */
+ public String qname() {
+ return new String(chars, 1, chars.length - 1);
+ }
+
+ /**
+ * Creates a local name string from qualified name.
+ *
+ * @return The local name string.
+ */
+ public String local() {
+ if (chars[0] != 0) {
+ return new String(chars, chars[0] + 1, chars.length - chars[0] - 1);
+ }
+ return new String(chars, 1, chars.length - 1);
+ }
+
+ /**
+ * Creates a prefix string from qualified name.
+ *
+ * @return The prefix string.
+ */
+ public String pref() {
+ if (chars[0] != 0) {
+ return new String(chars, 1, chars[0] - 1);
+ }
+ return "";
+ }
+
+ /**
+ * Compares two qualified name prefixes.
+ *
+ * @param qname A qualified name.
+ * @return true if prefixes are equal.
+ */
+ public boolean eqpref(char[] qname) {
+ if (chars[0] == qname[0]) {
+ char len = chars[0];
+ for (char i = 1; i < len; i += 1) {
+ if (chars[i] != qname[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Compares two qualified names.
+ *
+ * @param qname A qualified name.
+ * @return true if qualified names are equal.
+ */
+ public boolean eqname(char[] qname) {
+ char len = (char) chars.length;
+ if (len == qname.length) {
+ for (char i = 0; i < len; i += 1) {
+ if (chars[i] != qname[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/Parser.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,3367 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.SAXException;
+
+/**
+ * XML non-validating parser engine.
+ */
+public abstract class Parser {
+
+ public final static String FAULT = "";
+ protected final static int BUFFSIZE_READER = 512;
+ protected final static int BUFFSIZE_PARSER = 128;
+ /**
+ * The end of stream character.
+ */
+ public final static char EOS = 0xffff;
+ private Pair mNoNS; // there is no namespace
+ private Pair mXml; // the xml namespace
+ private Map<String, Input> mEnt; // the entities look up table
+ private Map<String, Input> mPEnt; // the parmeter entities look up table
+ protected boolean mIsSAlone; // xml decl standalone flag
+ protected boolean mIsSAloneSet; // standalone is explicitely set
+ protected boolean mIsNSAware; // if true - namespace aware mode
+ protected int mPh; // current phase of document processing
+ protected final static int PH_BEFORE_DOC = -1; // before parsing
+ protected final static int PH_DOC_START = 0; // document start
+ protected final static int PH_MISC_DTD = 1; // misc before DTD
+ protected final static int PH_DTD = 2; // DTD
+ protected final static int PH_DTD_MISC = 3; // misc after DTD
+ protected final static int PH_DOCELM = 4; // document's element
+ protected final static int PH_DOCELM_MISC = 5; // misc after element
+ protected final static int PH_AFTER_DOC = 6; // after parsing
+ protected int mEvt; // current event type
+ protected final static int EV_NULL = 0; // unknown
+ protected final static int EV_ELM = 1; // empty element
+ protected final static int EV_ELMS = 2; // start element
+ protected final static int EV_ELME = 3; // end element
+ protected final static int EV_TEXT = 4; // textual content
+ protected final static int EV_WSPC = 5; // white space content
+ protected final static int EV_PI = 6; // processing instruction
+ protected final static int EV_CDAT = 7; // character data
+ protected final static int EV_COMM = 8; // comment
+ protected final static int EV_DTD = 9; // document type definition
+ protected final static int EV_ENT = 10; // skipped entity
+ private char mESt; // built-in entity recognizer state
+ // mESt values:
+ // 0x100 : the initial state
+ // > 0x100 : unrecognized name
+ // < 0x100 : replacement character
+ protected char[] mBuff; // parser buffer
+ protected int mBuffIdx; // index of the last char
+ protected Pair mPref; // stack of prefixes
+ protected Pair mElm; // stack of elements
+ // mAttL.chars - element qname
+ // mAttL.next - next element
+ // mAttL.list - list of attributes defined on this element
+ // mAttL.list.chars - attribute qname
+ // mAttL.list.id - a char representing attribute's type see below
+ // mAttL.list.next - next attribute defined on the element
+ // mAttL.list.list - devault value structure or null
+ // mAttL.list.list.chars - "name='value' " chars array for Input
+ //
+ // Attribute type character values:
+ // 'i' - "ID"
+ // 'r' - "IDREF"
+ // 'R' - "IDREFS"
+ // 'n' - "ENTITY"
+ // 'N' - "ENTITIES"
+ // 't' - "NMTOKEN"
+ // 'T' - "NMTOKENS"
+ // 'u' - enumeration type
+ // 'o' - "NOTATION"
+ // 'c' - "CDATA"
+ // see also: bkeyword() and atype()
+ //
+ protected Pair mAttL; // list of defined attrs by element name
+ protected Input mDoc; // document entity
+ protected Input mInp; // stack of entities
+ private char[] mChars; // reading buffer
+ private int mChLen; // current capacity
+ private int mChIdx; // index to the next char
+ protected Attrs mAttrs; // attributes of the curr. element
+ private String[] mItems; // attributes array of the curr. element
+ private char mAttrIdx; // attributes counter/index
+ private String mUnent; // unresolved entity name
+ private Pair mDltd; // deleted objects for reuse
+ /**
+ * Default prefixes
+ */
+ private final static char NONS[];
+ private final static char XML[];
+ private final static char XMLNS[];
+
+ static {
+ NONS = new char[1];
+ NONS[0] = (char) 0;
+
+ XML = new char[4];
+ XML[0] = (char) 4;
+ XML[1] = 'x';
+ XML[2] = 'm';
+ XML[3] = 'l';
+
+ XMLNS = new char[6];
+ XMLNS[0] = (char) 6;
+ XMLNS[1] = 'x';
+ XMLNS[2] = 'm';
+ XMLNS[3] = 'l';
+ XMLNS[4] = 'n';
+ XMLNS[5] = 's';
+ }
+ /**
+ * ASCII character type array.
+ *
+ * This array maps an ASCII (7 bit) character to the character type.<br />
+ * Possible character type values are:<br /> - ' ' for any kind of white
+ * space character;<br /> - 'a' for any lower case alphabetical character
+ * value;<br /> - 'A' for any upper case alphabetical character value;<br />
+ * - 'd' for any decimal digit character value;<br /> - 'z' for any
+ * character less then ' ' except '\t', '\n', '\r';<br /> An ASCII (7 bit)
+ * character which does not fall in any category listed above is mapped to
+ * it self.
+ */
+ private static final byte asctyp[];
+ /**
+ * NMTOKEN character type array.
+ *
+ * This array maps an ASCII (7 bit) character to the character type.<br />
+ * Possible character type values are:<br /> - 0 for underscore ('_') or any
+ * lower and upper case alphabetical character value;<br /> - 1 for colon
+ * (':') character;<br /> - 2 for dash ('-') and dot ('.') or any decimal
+ * digit character value;<br /> - 3 for any kind of white space character<br
+ * /> An ASCII (7 bit) character which does not fall in any category listed
+ * above is mapped to 0xff.
+ */
+ private static final byte nmttyp[];
+
+ /**
+ * Static constructor.
+ *
+ * Sets up the ASCII character type array which is used by
+ * {@link #asctyp asctyp} method and NMTOKEN character type array.
+ */
+ static {
+ short i = 0;
+
+ asctyp = new byte[0x80];
+ while (i < ' ') {
+ asctyp[i++] = (byte) 'z';
+ }
+ asctyp['\t'] = (byte) ' ';
+ asctyp['\r'] = (byte) ' ';
+ asctyp['\n'] = (byte) ' ';
+ while (i < '0') {
+ asctyp[i] = (byte) i++;
+ }
+ while (i <= '9') {
+ asctyp[i++] = (byte) 'd';
+ }
+ while (i < 'A') {
+ asctyp[i] = (byte) i++;
+ }
+ while (i <= 'Z') {
+ asctyp[i++] = (byte) 'A';
+ }
+ while (i < 'a') {
+ asctyp[i] = (byte) i++;
+ }
+ while (i <= 'z') {
+ asctyp[i++] = (byte) 'a';
+ }
+ while (i < 0x80) {
+ asctyp[i] = (byte) i++;
+ }
+
+ nmttyp = new byte[0x80];
+ for (i = 0; i < '0'; i++) {
+ nmttyp[i] = (byte) 0xff;
+ }
+ while (i <= '9') {
+ nmttyp[i++] = (byte) 2; // digits
+ }
+ while (i < 'A') {
+ nmttyp[i++] = (byte) 0xff;
+ }
+ // skiped upper case alphabetical character are already 0
+ for (i = '['; i < 'a'; i++) {
+ nmttyp[i] = (byte) 0xff;
+ }
+ // skiped lower case alphabetical character are already 0
+ for (i = '{'; i < 0x80; i++) {
+ nmttyp[i] = (byte) 0xff;
+ }
+ nmttyp['_'] = 0;
+ nmttyp[':'] = 1;
+ nmttyp['.'] = 2;
+ nmttyp['-'] = 2;
+ nmttyp[' '] = 3;
+ nmttyp['\t'] = 3;
+ nmttyp['\r'] = 3;
+ nmttyp['\n'] = 3;
+ }
+
+ /**
+ * Constructor.
+ */
+ protected Parser() {
+ mPh = PH_BEFORE_DOC; // before parsing
+
+ // Initialize the parser
+ mBuff = new char[BUFFSIZE_PARSER];
+ mAttrs = new Attrs();
+
+ // Default namespace
+ mPref = pair(mPref);
+ mPref.name = "";
+ mPref.value = "";
+ mPref.chars = NONS;
+ mNoNS = mPref; // no namespace
+ // XML namespace
+ mPref = pair(mPref);
+ mPref.name = "xml";
+ mPref.value = "http://www.w3.org/XML/1998/namespace";
+ mPref.chars = XML;
+ mXml = mPref; // XML namespace
+ }
+
+ /**
+ * Initializes parser's internals. Note, current input has to be set before
+ * this method is called.
+ */
+ protected void init() {
+ mUnent = null;
+ mElm = null;
+ mPref = mXml;
+ mAttL = null;
+ mPEnt = new HashMap<>();
+ mEnt = new HashMap<>();
+ mDoc = mInp; // current input is document entity
+ mChars = mInp.chars; // use document entity buffer
+ mPh = PH_DOC_START; // the begining of the document
+ }
+
+ /**
+ * Cleans up parser internal resources.
+ */
+ protected void cleanup() {
+ // Default attributes
+ while (mAttL != null) {
+ while (mAttL.list != null) {
+ if (mAttL.list.list != null) {
+ del(mAttL.list.list);
+ }
+ mAttL.list = del(mAttL.list);
+ }
+ mAttL = del(mAttL);
+ }
+ // Element stack
+ while (mElm != null) {
+ mElm = del(mElm);
+ }
+ // Namespace prefixes
+ while (mPref != mXml) {
+ mPref = del(mPref);
+ }
+ // Inputs
+ while (mInp != null) {
+ pop();
+ }
+ // Document reader
+ if ((mDoc != null) && (mDoc.src != null)) {
+ try {
+ mDoc.src.close();
+ } catch (IOException ioe) {
+ }
+ }
+ mPEnt = null;
+ mEnt = null;
+ mDoc = null;
+ mPh = PH_AFTER_DOC; // before documnet processing
+ }
+
+ /**
+ * Processes a portion of document. This method returns one of EV_*
+ * constants as an identifier of the portion of document have been read.
+ *
+ * @return Identifier of processed document portion.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ protected int step() throws Exception {
+ mEvt = EV_NULL;
+ int st = 0;
+ while (mEvt == EV_NULL) {
+ char ch = (mChIdx < mChLen) ? mChars[mChIdx++] : getch();
+ switch (st) {
+ case 0: // all sorts of markup (dispetcher)
+ if (ch != '<') {
+ bkch();
+ mBuffIdx = -1; // clean parser buffer
+ st = 1;
+ break;
+ }
+ switch (getch()) {
+ case '/': // the end of the element content
+ mEvt = EV_ELME;
+ if (mElm == null) {
+ panic(FAULT);
+ }
+ // Check element's open/close tags balance
+ mBuffIdx = -1; // clean parser buffer
+ bname(mIsNSAware);
+ char[] chars = mElm.chars;
+ if (chars.length == (mBuffIdx + 1)) {
+ for (char i = 1; i <= mBuffIdx; i += 1) {
+ if (chars[i] != mBuff[i]) {
+ panic(FAULT);
+ }
+ }
+ } else {
+ panic(FAULT);
+ }
+ // Skip white spaces before '>'
+ if (wsskip() != '>') {
+ panic(FAULT);
+ }
+ getch(); // read '>'
+ break;
+
+ case '!': // a comment or a CDATA
+ ch = getch();
+ bkch();
+ switch (ch) {
+ case '-': // must be a comment
+ mEvt = EV_COMM;
+ comm();
+ break;
+
+ case '[': // must be a CDATA section
+ mEvt = EV_CDAT;
+ cdat();
+ break;
+
+ default: // must be 'DOCTYPE'
+ mEvt = EV_DTD;
+ dtd();
+ break;
+ }
+ break;
+
+ case '?': // processing instruction
+ mEvt = EV_PI;
+ pi();
+ break;
+
+ default: // must be the first char of an xml name
+ bkch();
+ // Read an element name and put it on top of the
+ // element stack
+ mElm = pair(mElm); // add new element to the stack
+ mElm.chars = qname(mIsNSAware);
+ mElm.name = mElm.local();
+ mElm.id = (mElm.next != null) ? mElm.next.id : 0; // flags
+ mElm.num = 0; // namespace counter
+ // Find the list of defined attributs of the current
+ // element
+ Pair elm = find(mAttL, mElm.chars);
+ mElm.list = (elm != null) ? elm.list : null;
+ // Read attributes till the end of the element tag
+ mAttrIdx = 0;
+ Pair att = pair(null);
+ att.num = 0; // clear attribute's flags
+ attr(att); // get all attributes inc. defaults
+ del(att);
+ mElm.value = (mIsNSAware) ? rslv(mElm.chars) : null;
+ // Skip white spaces before '>'
+ switch (wsskip()) {
+ case '>':
+ getch(); // read '>'
+ mEvt = EV_ELMS;
+ break;
+
+ case '/':
+ getch(); // read '/'
+ if (getch() != '>') // read '>'
+ {
+ panic(FAULT);
+ }
+ mEvt = EV_ELM;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+ }
+ break;
+
+ case 1: // read white space
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\n':
+ bappend(ch);
+ break;
+
+ case '\r': // EOL processing [#2.11]
+ if (getch() != '\n') {
+ bkch();
+ }
+ bappend('\n');
+ break;
+
+ case '<':
+ mEvt = EV_WSPC;
+ bkch();
+ bflash_ws();
+ break;
+
+ default:
+ bkch();
+ st = 2;
+ break;
+ }
+ break;
+
+ case 2: // read the text content of the element
+ switch (ch) {
+ case '&':
+ if (mUnent == null) {
+ // There was no unresolved entity on previous step.
+ if ((mUnent = ent('x')) != null) {
+ mEvt = EV_TEXT;
+ bkch(); // move back to ';' after entity name
+ setch('&'); // parser must be back on next step
+ bflash();
+ }
+ } else {
+ // There was unresolved entity on previous step.
+ mEvt = EV_ENT;
+ skippedEnt(mUnent);
+ mUnent = null;
+ }
+ break;
+
+ case '<':
+ mEvt = EV_TEXT;
+ bkch();
+ bflash();
+ break;
+
+ case '\r': // EOL processing [#2.11]
+ if (getch() != '\n') {
+ bkch();
+ }
+ bappend('\n');
+ break;
+
+ case EOS:
+ panic(FAULT);
+
+ default:
+ bappend(ch);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+
+ return mEvt;
+ }
+
+ /**
+ * Parses the document type declaration.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void dtd() throws Exception {
+ char ch;
+ String str = null;
+ String name = null;
+ Pair psid = null;
+ // read 'DOCTYPE'
+ if ("DOCTYPE".equals(name(false)) != true) {
+ panic(FAULT);
+ }
+ mPh = PH_DTD; // DTD
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // read the document type name
+ if (chtyp(ch) != ' ') {
+ bkch();
+ name = name(mIsNSAware);
+ wsskip();
+ st = 1; // read 'PUPLIC' or 'SYSTEM'
+ }
+ break;
+
+ case 1: // read 'PUPLIC' or 'SYSTEM'
+ switch (chtyp(ch)) {
+ case 'A':
+ bkch();
+ psid = pubsys(' ');
+ st = 2; // skip spaces before internal subset
+ docType(name, psid.name, psid.value);
+ break;
+
+ case '[':
+ bkch();
+ st = 2; // skip spaces before internal subset
+ docType(name, null, null);
+ break;
+
+ case '>':
+ bkch();
+ st = 3; // skip spaces after internal subset
+ docType(name, null, null);
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 2: // skip spaces before internal subset
+ switch (chtyp(ch)) {
+ case '[':
+ // Process internal subset
+ dtdsub();
+ st = 3; // skip spaces after internal subset
+ break;
+
+ case '>':
+ // There is no internal subset
+ bkch();
+ st = 3; // skip spaces after internal subset
+ break;
+
+ case ' ':
+ // skip white spaces
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 3: // skip spaces after internal subset
+ switch (chtyp(ch)) {
+ case '>':
+ if (psid != null) {
+ // Report the DTD external subset
+ InputSource is = resolveEnt(name, psid.name, psid.value);
+ if (is != null) {
+ if (mIsSAlone == false) {
+ // Set the end of DTD external subset char
+ bkch();
+ setch(']');
+ // Set the DTD external subset InputSource
+ push(new Input(BUFFSIZE_READER));
+ setinp(is);
+ mInp.pubid = psid.name;
+ mInp.sysid = psid.value;
+ // Parse the DTD external subset
+ dtdsub();
+ } else {
+ // Unresolved DTD external subset
+ skippedEnt("[dtd]");
+ // Release reader and stream
+ if (is.getCharacterStream() != null) {
+ try {
+ is.getCharacterStream().close();
+ } catch (IOException ioe) {
+ }
+ }
+ if (is.getByteStream() != null) {
+ try {
+ is.getByteStream().close();
+ } catch (IOException ioe) {
+ }
+ }
+ }
+ } else {
+ // Unresolved DTD external subset
+ skippedEnt("[dtd]");
+ }
+ del(psid);
+ }
+ st = -1; // end of DTD
+ break;
+
+ case ' ':
+ // skip white spaces
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Parses the document type declaration subset.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void dtdsub() throws Exception {
+ char ch;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // skip white spaces before a declaration
+ switch (chtyp(ch)) {
+ case '<':
+ ch = getch();
+ switch (ch) {
+ case '?':
+ pi();
+ break;
+
+ case '!':
+ ch = getch();
+ bkch();
+ if (ch == '-') {
+ comm();
+ break;
+ }
+ // A markup or an entity declaration
+ bntok();
+ switch (bkeyword()) {
+ case 'n':
+ dtdent();
+ break;
+
+ case 'a':
+ dtdattl(); // parse attributes declaration
+ break;
+
+ case 'e':
+ dtdelm(); // parse element declaration
+ break;
+
+ case 'o':
+ dtdnot(); // parse notation declaration
+ break;
+
+ default:
+ panic(FAULT); // unsupported markup declaration
+ break;
+ }
+ st = 1; // read the end of declaration
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case '%':
+ // A parameter entity reference
+ pent(' ');
+ break;
+
+ case ']':
+ // End of DTD subset
+ st = -1;
+ break;
+
+ case ' ':
+ // Skip white spaces
+ break;
+
+ case 'Z':
+ // End of stream
+ if (getch() != ']') {
+ panic(FAULT);
+ }
+ st = -1;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 1: // read the end of declaration
+ switch (ch) {
+ case '>': // there is no notation
+ st = 0; // skip white spaces before a declaration
+ break;
+
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ // Skip white spaces
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Parses an entity declaration. This method fills the general (
+ * <code>mEnt</code>) and parameter
+ * (
+ * <code>mPEnt</code>) entity look up table.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void dtdent() throws Exception {
+ String str = null;
+ char[] val = null;
+ Input inp = null;
+ Pair ids = null;
+ char ch;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // skip white spaces before entity name
+ switch (chtyp(ch)) {
+ case ' ':
+ // Skip white spaces
+ break;
+
+ case '%':
+ // Parameter entity or parameter entity declaration.
+ ch = getch();
+ bkch();
+ if (chtyp(ch) == ' ') {
+ // Parameter entity declaration.
+ wsskip();
+ str = name(false);
+ switch (chtyp(wsskip())) {
+ case 'A':
+ // Read the external identifier
+ ids = pubsys(' ');
+ if (wsskip() == '>') {
+ // External parsed entity
+ if (mPEnt.containsKey(str) == false) { // [#4.2]
+ inp = new Input();
+ inp.pubid = ids.name;
+ inp.sysid = ids.value;
+ mPEnt.put(str, inp);
+ }
+ } else {
+ panic(FAULT);
+ }
+ del(ids);
+ st = -1; // the end of declaration
+ break;
+
+ case '\"':
+ case '\'':
+ // Read the parameter entity value
+ bqstr('d');
+ // Create the parameter entity value
+ val = new char[mBuffIdx + 1];
+ System.arraycopy(mBuff, 1, val, 1, val.length - 1);
+ // Add surrounding spaces [#4.4.8]
+ val[0] = ' ';
+ // Add the entity to the entity look up table
+ if (mPEnt.containsKey(str) == false) { // [#4.2]
+ inp = new Input(val);
+ inp.pubid = mInp.pubid;
+ inp.sysid = mInp.sysid;
+ inp.xmlenc = mInp.xmlenc;
+ inp.xmlver = mInp.xmlver;
+ mPEnt.put(str, inp);
+ }
+ st = -1; // the end of declaration
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ } else {
+ // Parameter entity reference.
+ pent(' ');
+ }
+ break;
+
+ default:
+ bkch();
+ str = name(false);
+ st = 1; // read entity declaration value
+ break;
+ }
+ break;
+
+ case 1: // read entity declaration value
+ switch (chtyp(ch)) {
+ case '\"': // internal entity
+ case '\'':
+ bkch();
+ bqstr('d'); // read a string into the buffer
+ if (mEnt.get(str) == null) {
+ // Create general entity value
+ val = new char[mBuffIdx];
+ System.arraycopy(mBuff, 1, val, 0, val.length);
+ // Add the entity to the entity look up table
+ if (mEnt.containsKey(str) == false) { // [#4.2]
+ inp = new Input(val);
+ inp.pubid = mInp.pubid;
+ inp.sysid = mInp.sysid;
+ inp.xmlenc = mInp.xmlenc;
+ inp.xmlver = mInp.xmlver;
+ mEnt.put(str, inp);
+ }
+ }
+ st = -1; // the end of declaration
+ break;
+
+ case 'A': // external entity
+ bkch();
+ ids = pubsys(' ');
+ switch (wsskip()) {
+ case '>': // external parsed entity
+ if (mEnt.containsKey(str) == false) { // [#4.2]
+ inp = new Input();
+ inp.pubid = ids.name;
+ inp.sysid = ids.value;
+ mEnt.put(str, inp);
+ }
+ break;
+
+ case 'N': // external general unparsed entity
+ if ("NDATA".equals(name(false)) == true) {
+ wsskip();
+ unparsedEntDecl(str, ids.name, ids.value, name(false));
+ break;
+ }
+ default:
+ panic(FAULT);
+ break;
+ }
+ del(ids);
+ st = -1; // the end of declaration
+ break;
+
+ case ' ':
+ // Skip white spaces
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Parses an element declaration.
+ *
+ * This method parses the declaration up to the closing angle bracket.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void dtdelm() throws Exception {
+ // This is stub implementation which skips an element
+ // declaration.
+ wsskip();
+ name(mIsNSAware);
+
+ char ch;
+ while (true) {
+ ch = getch();
+ switch (ch) {
+ case '>':
+ bkch();
+ return;
+
+ case EOS:
+ panic(FAULT);
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parses an attribute list declaration.
+ *
+ * This method parses the declaration up to the closing angle bracket.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void dtdattl() throws Exception {
+ char elmqn[] = null;
+ Pair elm = null;
+ char ch;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // read the element name
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case '_':
+ case 'X':
+ case ':':
+ bkch();
+ // Get the element from the list or add a new one.
+ elmqn = qname(mIsNSAware);
+ elm = find(mAttL, elmqn);
+ if (elm == null) {
+ elm = pair(mAttL);
+ elm.chars = elmqn;
+ mAttL = elm;
+ }
+ st = 1; // read an attribute declaration
+ break;
+
+ case ' ':
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case 1: // read an attribute declaration
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case '_':
+ case 'X':
+ case ':':
+ bkch();
+ dtdatt(elm);
+ if (wsskip() == '>') {
+ return;
+ }
+ break;
+
+ case ' ':
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parses an attribute declaration.
+ *
+ * The attribute uses the following fields of Pair object: chars - characters
+ * of qualified name id - the type identifier of the attribute list - a pair
+ * which holds the default value (chars field)
+ *
+ * @param elm An object which represents all defined attributes on an
+ * element.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void dtdatt(Pair elm) throws Exception {
+ char attqn[] = null;
+ Pair att = null;
+ char ch;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // the attribute name
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case '_':
+ case 'X':
+ case ':':
+ bkch();
+ // Get the attribut from the list or add a new one.
+ attqn = qname(mIsNSAware);
+ att = find(elm.list, attqn);
+ if (att == null) {
+ // New attribute declaration
+ att = pair(elm.list);
+ att.chars = attqn;
+ elm.list = att;
+ } else {
+ // Do not override the attribute declaration [#3.3]
+ att = pair(null);
+ att.chars = attqn;
+ att.id = 'c';
+ }
+ wsskip();
+ st = 1;
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ case ' ':
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case 1: // the attribute type
+ switch (chtyp(ch)) {
+ case '(':
+ att.id = 'u'; // enumeration type
+ st = 2; // read the first element of the list
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ case ' ':
+ break;
+
+ default:
+ bkch();
+ bntok(); // read type id
+ att.id = bkeyword();
+ switch (att.id) {
+ case 'o': // NOTATION
+ if (wsskip() != '(') {
+ panic(FAULT);
+ }
+ ch = getch();
+ st = 2; // read the first element of the list
+ break;
+
+ case 'i': // ID
+ case 'r': // IDREF
+ case 'R': // IDREFS
+ case 'n': // ENTITY
+ case 'N': // ENTITIES
+ case 't': // NMTOKEN
+ case 'T': // NMTOKENS
+ case 'c': // CDATA
+ wsskip();
+ st = 4; // read default declaration
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 2: // read the first element of the list
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case 'd':
+ case '.':
+ case ':':
+ case '-':
+ case '_':
+ case 'X':
+ bkch();
+ switch (att.id) {
+ case 'u': // enumeration type
+ bntok();
+ break;
+
+ case 'o': // NOTATION
+ mBuffIdx = -1;
+ bname(false);
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ wsskip();
+ st = 3; // read next element of the list
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ case ' ':
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case 3: // read next element of the list
+ switch (ch) {
+ case ')':
+ wsskip();
+ st = 4; // read default declaration
+ break;
+
+ case '|':
+ wsskip();
+ switch (att.id) {
+ case 'u': // enumeration type
+ bntok();
+ break;
+
+ case 'o': // NOTATION
+ mBuffIdx = -1;
+ bname(false);
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ wsskip();
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case 4: // read default declaration
+ switch (ch) {
+ case '#':
+ bntok();
+ switch (bkeyword()) {
+ case 'F': // FIXED
+ switch (wsskip()) {
+ case '\"':
+ case '\'':
+ st = 5; // read the default value
+ break;
+
+ case EOS:
+ panic(FAULT);
+
+ default:
+ st = -1;
+ break;
+ }
+ break;
+
+ case 'Q': // REQUIRED
+ case 'I': // IMPLIED
+ st = -1;
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case '\"':
+ case '\'':
+ bkch();
+ st = 5; // read the default value
+ break;
+
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ default:
+ bkch();
+ st = -1;
+ break;
+ }
+ break;
+
+ case 5: // read the default value
+ switch (ch) {
+ case '\"':
+ case '\'':
+ bkch();
+ bqstr('d'); // the value in the mBuff now
+ att.list = pair(null);
+ // Create a string like "attqname='value' "
+ att.list.chars = new char[att.chars.length + mBuffIdx + 3];
+ System.arraycopy(
+ att.chars, 1, att.list.chars, 0, att.chars.length - 1);
+ att.list.chars[att.chars.length - 1] = '=';
+ att.list.chars[att.chars.length] = ch;
+ System.arraycopy(
+ mBuff, 1, att.list.chars, att.chars.length + 1, mBuffIdx);
+ att.list.chars[att.chars.length + mBuffIdx + 1] = ch;
+ att.list.chars[att.chars.length + mBuffIdx + 2] = ' ';
+ st = -1;
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parses a notation declaration.
+ *
+ * This method parses the declaration up to the closing angle bracket.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void dtdnot() throws Exception {
+ wsskip();
+ String name = name(false);
+ wsskip();
+ Pair ids = pubsys('N');
+ notDecl(name, ids.name, ids.value);
+ del(ids);
+ }
+
+ /**
+ * Parses an attribute.
+ *
+ * This recursive method is responsible for prefix addition
+ * (
+ * <code>mPref</code>) on the way down. The element's start tag end triggers
+ * the return process. The method then on it's way back resolves prefixes
+ * and accumulates attributes.
+ *
+ * <p><code>att.num</code> carries attribute flags where: 0x1 - attribute is
+ * declared in DTD (attribute decalration had been read); 0x2 - attribute's
+ * default value is used.</p>
+ *
+ * @param att An object which reprecents current attribute.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void attr(Pair att) throws Exception {
+ switch (wsskip()) {
+ case '/':
+ case '>':
+ if ((att.num & 0x2) == 0) { // all attributes have been read
+ att.num |= 0x2; // set default attribute flag
+ Input inp = mInp;
+ // Go through all attributes defined on current element.
+ for (Pair def = mElm.list; def != null; def = def.next) {
+ if (def.list == null) // no default value
+ {
+ continue;
+ }
+ // Go through all attributes defined on current
+ // element and add defaults.
+ Pair act = find(att.next, def.chars);
+ if (act == null) {
+ push(new Input(def.list.chars));
+ }
+ }
+ if (mInp != inp) { // defaults have been added
+ attr(att);
+ return;
+ }
+ }
+ // Ensure the attribute string array capacity
+ mAttrs.setLength(mAttrIdx);
+ mItems = mAttrs.mItems;
+ return;
+
+ case EOS:
+ panic(FAULT);
+
+ default:
+ // Read the attribute name and value
+ att.chars = qname(mIsNSAware);
+ att.name = att.local();
+ String type = atype(att); // sets attribute's type on att.id
+ wsskip();
+ if (getch() != '=') {
+ panic(FAULT);
+ }
+ bqstr((char) att.id); // read the value with normalization.
+ String val = new String(mBuff, 1, mBuffIdx);
+ Pair next = pair(att);
+ next.num = (att.num & ~0x1); // inherit attribute flags
+ // Put a namespace declaration on top of the prefix stack
+ if ((mIsNSAware == false) || (isdecl(att, val) == false)) {
+ // An ordinary attribute
+ mAttrIdx++;
+ attr(next); // recursive call to parse the next attribute
+ mAttrIdx--;
+ // Add the attribute to the attributes string array
+ char idx = (char) (mAttrIdx << 3);
+ mItems[idx + 1] = att.qname(); // attr qname
+ mItems[idx + 2] = (mIsNSAware) ? att.name : ""; // attr local name
+ mItems[idx + 3] = val; // attr value
+ mItems[idx + 4] = type; // attr type
+ switch (att.num & 0x3) {
+ case 0x0:
+ mItems[idx + 5] = null;
+ break;
+
+ case 0x1: // declared attribute
+ mItems[idx + 5] = "d";
+ break;
+
+ default: // 0x2, 0x3 - default attribute always declared
+ mItems[idx + 5] = "D";
+ break;
+ }
+ // Resolve the prefix if any and report the attribute
+ // NOTE: The attribute does not accept the default namespace.
+ mItems[idx + 0] = (att.chars[0] != 0) ? rslv(att.chars) : "";
+ } else {
+ // A namespace declaration. mPref.name contains prefix and
+ // mPref.value contains namespace URI set by isdecl method.
+ // Report a start of the new mapping
+ newPrefix();
+ // Recursive call to parse the next attribute
+ attr(next);
+ // NOTE: The namespace declaration is not reported.
+ }
+ del(next);
+ break;
+ }
+ }
+
+ /**
+ * Retrieves attribute type.
+ *
+ * This method sets the type of normalization in the attribute
+ * <code>id</code> field and returns the name of attribute type.
+ *
+ * @param att An object which represents current attribute.
+ * @return The name of the attribute type.
+ * @exception Exception is parser specific exception form panic method.
+ */
+ private String atype(Pair att)
+ throws Exception {
+ Pair attr;
+
+ // CDATA-type normalization by default [#3.3.3]
+ att.id = 'c';
+ if (mElm.list == null || (attr = find(mElm.list, att.chars)) == null) {
+ return "CDATA";
+ }
+
+ att.num |= 0x1; // attribute is declared
+
+ // Non-CDATA normalization except when the attribute type is CDATA.
+ att.id = 'i';
+ switch (attr.id) {
+ case 'i':
+ return "ID";
+
+ case 'r':
+ return "IDREF";
+
+ case 'R':
+ return "IDREFS";
+
+ case 'n':
+ return "ENTITY";
+
+ case 'N':
+ return "ENTITIES";
+
+ case 't':
+ return "NMTOKEN";
+
+ case 'T':
+ return "NMTOKENS";
+
+ case 'u':
+ return "NMTOKEN";
+
+ case 'o':
+ return "NOTATION";
+
+ case 'c':
+ att.id = 'c';
+ return "CDATA";
+
+ default:
+ panic(FAULT);
+ }
+ return null;
+ }
+
+ /**
+ * Parses a comment.
+ *
+ * The '<!' part is read in dispatcher so the method starts
+ * with first '-' after '<!'.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ */
+ @SuppressWarnings("fallthrough")
+ private void comm() throws Exception {
+ if (mPh == PH_DOC_START) {
+ mPh = PH_MISC_DTD; // misc before DTD
+ } // '<!' has been already read by dispetcher.
+ char ch;
+ mBuffIdx = -1;
+ for (short st = 0; st >= 0;) {
+ ch = (mChIdx < mChLen) ? mChars[mChIdx++] : getch();
+ if (ch == EOS) {
+ panic(FAULT);
+ }
+ switch (st) {
+ case 0: // first '-' of the comment open
+ if (ch == '-') {
+ st = 1;
+ } else {
+ panic(FAULT);
+ }
+ break;
+
+ case 1: // secind '-' of the comment open
+ if (ch == '-') {
+ st = 2;
+ } else {
+ panic(FAULT);
+ }
+ break;
+
+ case 2: // skip the comment body
+ switch (ch) {
+ case '-':
+ st = 3;
+ break;
+
+ default:
+ bappend(ch);
+ break;
+ }
+ break;
+
+ case 3: // second '-' of the comment close
+ switch (ch) {
+ case '-':
+ st = 4;
+ break;
+
+ default:
+ bappend('-');
+ bappend(ch);
+ st = 2;
+ break;
+ }
+ break;
+
+ case 4: // '>' of the comment close
+ if (ch == '>') {
+ comm(mBuff, mBuffIdx + 1);
+ st = -1;
+ break;
+ }
+ // else - panic [#2.5 compatibility note]
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Parses a processing instruction.
+ *
+ * The '<?' is read in dispatcher so the method starts with
+ * first character of PI target name after '<?'.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void pi() throws Exception {
+ // '<?' has been already read by dispetcher.
+ char ch;
+ String str = null;
+ mBuffIdx = -1;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ if (ch == EOS) {
+ panic(FAULT);
+ }
+ switch (st) {
+ case 0: // read the PI target name
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case '_':
+ case ':':
+ case 'X':
+ bkch();
+ str = name(false);
+ // PI target name may not be empty string [#2.6]
+ // PI target name 'XML' is reserved [#2.6]
+ if ((str.length() == 0)
+ || (mXml.name.equals(str.toLowerCase()) == true)) {
+ panic(FAULT);
+ }
+ // This is processing instruction
+ if (mPh == PH_DOC_START) // the begining of the document
+ {
+ mPh = PH_MISC_DTD; // misc before DTD
+ }
+ wsskip(); // skip spaces after the PI target name
+ st = 1; // accumulate the PI body
+ mBuffIdx = -1;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 1: // accumulate the PI body
+ switch (ch) {
+ case '?':
+ st = 2; // end of the PI body
+ break;
+
+ default:
+ bappend(ch);
+ break;
+ }
+ break;
+
+ case 2: // end of the PI body
+ switch (ch) {
+ case '>':
+ // PI has been read.
+ pi(str, new String(mBuff, 0, mBuffIdx + 1));
+ st = -1;
+ break;
+
+ case '?':
+ bappend('?');
+ break;
+
+ default:
+ bappend('?');
+ bappend(ch);
+ st = 1; // accumulate the PI body
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Parses a character data.
+ *
+ * The '<!' part is read in dispatcher so the method starts
+ * with first '[' after '<!'.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void cdat()
+ throws Exception {
+ // '<!' has been already read by dispetcher.
+ char ch;
+ mBuffIdx = -1;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // the first '[' of the CDATA open
+ if (ch == '[') {
+ st = 1;
+ } else {
+ panic(FAULT);
+ }
+ break;
+
+ case 1: // read "CDATA"
+ if (chtyp(ch) == 'A') {
+ bappend(ch);
+ } else {
+ if ("CDATA".equals(
+ new String(mBuff, 0, mBuffIdx + 1)) != true) {
+ panic(FAULT);
+ }
+ bkch();
+ st = 2;
+ }
+ break;
+
+ case 2: // the second '[' of the CDATA open
+ if (ch != '[') {
+ panic(FAULT);
+ }
+ mBuffIdx = -1;
+ st = 3;
+ break;
+
+ case 3: // read data before the first ']'
+ if (ch != ']') {
+ bappend(ch);
+ } else {
+ st = 4;
+ }
+ break;
+
+ case 4: // read the second ']' or continue to read the data
+ if (ch != ']') {
+ bappend(']');
+ bappend(ch);
+ st = 3;
+ } else {
+ st = 5;
+ }
+ break;
+
+ case 5: // read '>' or continue to read the data
+ switch (ch) {
+ case ']':
+ bappend(']');
+ break;
+
+ case '>':
+ bflash();
+ st = -1;
+ break;
+
+ default:
+ bappend(']');
+ bappend(']');
+ bappend(ch);
+ st = 3;
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Reads a xml name.
+ *
+ * The xml name must conform "Namespaces in XML" specification. Therefore
+ * the ':' character is not allowed in the name. This method should be used
+ * for PI and entity names which may not have a namespace according to the
+ * specification mentioned above.
+ *
+ * @param ns The true value turns namespace conformance on.
+ * @return The name has been read.
+ * @exception Exception When incorrect character appear in the name.
+ * @exception IOException
+ */
+ protected String name(boolean ns)
+ throws Exception {
+ mBuffIdx = -1;
+ bname(ns);
+ return new String(mBuff, 1, mBuffIdx);
+ }
+
+ /**
+ * Reads a qualified xml name.
+ *
+ * The characters of a qualified name is an array of characters. The first
+ * (chars[0]) character is the index of the colon character which separates
+ * the prefix from the local name. If the index is zero, the name does not
+ * contain separator or the parser works in the namespace unaware mode. The
+ * length of qualified name is the length of the array minus one.
+ *
+ * @param ns The true value turns namespace conformance on.
+ * @return The characters of a qualified name.
+ * @exception Exception When incorrect character appear in the name.
+ * @exception IOException
+ */
+ protected char[] qname(boolean ns)
+ throws Exception {
+ mBuffIdx = -1;
+ bname(ns);
+ char chars[] = new char[mBuffIdx + 1];
+ System.arraycopy(mBuff, 0, chars, 0, mBuffIdx + 1);
+ return chars;
+ }
+
+ /**
+ * Reads the public or/and system identifiers.
+ *
+ * @param inp The input object.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void pubsys(Input inp)
+ throws Exception {
+ Pair pair = pubsys(' ');
+ inp.pubid = pair.name;
+ inp.sysid = pair.value;
+ del(pair);
+ }
+
+ /**
+ * Reads the public or/and system identifiers.
+ *
+ * @param flag The 'N' allows public id be without system id.
+ * @return The public or/and system identifiers pair.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private Pair pubsys(char flag) throws Exception {
+ Pair ids = pair(null);
+ String str = name(false);
+ if ("PUBLIC".equals(str) == true) {
+ bqstr('i'); // non-CDATA normalization [#4.2.2]
+ ids.name = new String(mBuff, 1, mBuffIdx);
+ switch (wsskip()) {
+ case '\"':
+ case '\'':
+ bqstr(' ');
+ ids.value = new String(mBuff, 1, mBuffIdx);
+ break;
+
+ case EOS:
+ panic(FAULT);
+
+ default:
+ if (flag != 'N') // [#4.7]
+ {
+ panic(FAULT);
+ }
+ ids.value = null;
+ break;
+ }
+ return ids;
+ } else if ("SYSTEM".equals(str) == true) {
+ ids.name = null;
+ bqstr(' ');
+ ids.value = new String(mBuff, 1, mBuffIdx);
+ return ids;
+ }
+ panic(FAULT);
+ return null;
+ }
+
+ /**
+ * Reads an attribute value.
+ *
+ * The grammar which this method can read is:<br />
+ * <code>eqstr := S "=" qstr</code><br />
+ * <code>qstr := S ("'" string "'") |
+ * ('"' string '"')</code><br /> This method resolves entities
+ * inside a string unless the parser parses DTD.
+ *
+ * @param flag The '=' character forces the method to accept the '='
+ * character before quoted string and read the following string as not an
+ * attribute ('-'), 'c' - CDATA, 'i' - non CDATA, ' ' - no normalization;
+ * '-' - not an attribute value; 'd' - in DTD context.
+ * @return The content of the quoted strign as a string.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ protected String eqstr(char flag) throws Exception {
+ if (flag == '=') {
+ wsskip();
+ if (getch() != '=') {
+ panic(FAULT);
+ }
+ }
+ bqstr((flag == '=') ? '-' : flag);
+ return new String(mBuff, 1, mBuffIdx);
+ }
+
+ /**
+ * Resoves an entity.
+ *
+ * This method resolves built-in and character entity references. It is also
+ * reports external entities to the application.
+ *
+ * @param flag The 'x' character forces the method to report a skipped
+ * entity; 'i' character - indicates non-CDATA normalization.
+ * @return Name of unresolved entity or <code>null</code> if entity had been
+ * resolved successfully.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private String ent(char flag) throws Exception {
+ char ch;
+ int idx = mBuffIdx + 1;
+ Input inp = null;
+ String str = null;
+ mESt = 0x100; // reset the built-in entity recognizer
+ bappend('&');
+ for (short st = 0; st >= 0;) {
+ ch = (mChIdx < mChLen) ? mChars[mChIdx++] : getch();
+ switch (st) {
+ case 0: // the first character of the entity name
+ case 1: // read built-in entity name
+ switch (chtyp(ch)) {
+ case 'd':
+ case '.':
+ case '-':
+ if (st != 1) {
+ panic(FAULT);
+ }
+ case 'a':
+ case 'A':
+ case '_':
+ case 'X':
+ bappend(ch);
+ eappend(ch);
+ st = 1;
+ break;
+
+ case ':':
+ if (mIsNSAware != false) {
+ panic(FAULT);
+ }
+ bappend(ch);
+ eappend(ch);
+ st = 1;
+ break;
+
+ case ';':
+ if (mESt < 0x100) {
+ // The entity is a built-in entity
+ mBuffIdx = idx - 1;
+ bappend(mESt);
+ st = -1;
+ break;
+ } else if (mPh == PH_DTD) {
+ // In DTD entity declaration has to resolve character
+ // entities and include "as is" others. [#4.4.7]
+ bappend(';');
+ st = -1;
+ break;
+ }
+ // Convert an entity name to a string
+ str = new String(mBuff, idx + 1, mBuffIdx - idx);
+ inp = mEnt.get(str);
+ // Restore the buffer offset
+ mBuffIdx = idx - 1;
+ if (inp != null) {
+ if (inp.chars == null) {
+ // External entity
+ InputSource is = resolveEnt(str, inp.pubid, inp.sysid);
+ if (is != null) {
+ push(new Input(BUFFSIZE_READER));
+ setinp(is);
+ mInp.pubid = inp.pubid;
+ mInp.sysid = inp.sysid;
+ str = null; // the entity is resolved
+ } else {
+ // Unresolved external entity
+ if (flag != 'x') {
+ panic(FAULT); // unknown entity within marckup
+ } // str is name of unresolved entity
+ }
+ } else {
+ // Internal entity
+ push(inp);
+ str = null; // the entity is resolved
+ }
+ } else {
+ // Unknown or general unparsed entity
+ if (flag != 'x') {
+ panic(FAULT); // unknown entity within marckup
+ } // str is name of unresolved entity
+ }
+ st = -1;
+ break;
+
+ case '#':
+ if (st != 0) {
+ panic(FAULT);
+ }
+ st = 2;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 2: // read character entity
+ switch (chtyp(ch)) {
+ case 'd':
+ bappend(ch);
+ break;
+
+ case ';':
+ // Convert the character entity to a character
+ try {
+ int i = Integer.parseInt(
+ new String(mBuff, idx + 1, mBuffIdx - idx), 10);
+ if (i >= 0xffff) {
+ panic(FAULT);
+ }
+ ch = (char) i;
+ } catch (NumberFormatException nfe) {
+ panic(FAULT);
+ }
+ // Restore the buffer offset
+ mBuffIdx = idx - 1;
+ if (ch == ' ' || mInp.next != null) {
+ bappend(ch, flag);
+ } else {
+ bappend(ch);
+ }
+ st = -1;
+ break;
+
+ case 'a':
+ // If the entity buffer is empty and ch == 'x'
+ if ((mBuffIdx == idx) && (ch == 'x')) {
+ st = 3;
+ break;
+ }
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 3: // read hex character entity
+ switch (chtyp(ch)) {
+ case 'A':
+ case 'a':
+ case 'd':
+ bappend(ch);
+ break;
+
+ case ';':
+ // Convert the character entity to a character
+ try {
+ int i = Integer.parseInt(
+ new String(mBuff, idx + 1, mBuffIdx - idx), 16);
+ if (i >= 0xffff) {
+ panic(FAULT);
+ }
+ ch = (char) i;
+ } catch (NumberFormatException nfe) {
+ panic(FAULT);
+ }
+ // Restore the buffer offset
+ mBuffIdx = idx - 1;
+ if (ch == ' ' || mInp.next != null) {
+ bappend(ch, flag);
+ } else {
+ bappend(ch);
+ }
+ st = -1;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+
+ return str;
+ }
+
+ /**
+ * Resoves a parameter entity.
+ *
+ * This method resolves a parameter entity references. It is also reports
+ * external entities to the application.
+ *
+ * @param flag The '-' instruct the method to do not set up surrounding
+ * spaces [#4.4.8].
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void pent(char flag) throws Exception {
+ char ch;
+ int idx = mBuffIdx + 1;
+ Input inp = null;
+ String str = null;
+ bappend('%');
+ if (mPh != PH_DTD) // the DTD internal subset
+ {
+ return; // Not Recognized [#4.4.1]
+ } // Read entity name
+ bname(false);
+ str = new String(mBuff, idx + 2, mBuffIdx - idx - 1);
+ if (getch() != ';') {
+ panic(FAULT);
+ }
+ inp = mPEnt.get(str);
+ // Restore the buffer offset
+ mBuffIdx = idx - 1;
+ if (inp != null) {
+ if (inp.chars == null) {
+ // External parameter entity
+ InputSource is = resolveEnt(str, inp.pubid, inp.sysid);
+ if (is != null) {
+ if (flag != '-') {
+ bappend(' '); // tail space
+ }
+ push(new Input(BUFFSIZE_READER));
+ // BUG: there is no leading space! [#4.4.8]
+ setinp(is);
+ mInp.pubid = inp.pubid;
+ mInp.sysid = inp.sysid;
+ } else {
+ // Unresolved external parameter entity
+ skippedEnt("%" + str);
+ }
+ } else {
+ // Internal parameter entity
+ if (flag == '-') {
+ // No surrounding spaces
+ inp.chIdx = 1;
+ } else {
+ // Insert surrounding spaces
+ bappend(' '); // tail space
+ inp.chIdx = 0;
+ }
+ push(inp);
+ }
+ } else {
+ // Unknown parameter entity
+ skippedEnt("%" + str);
+ }
+ }
+
+ /**
+ * Recognizes and handles a namespace declaration.
+ *
+ * This method identifies a type of namespace declaration if any and puts
+ * new mapping on top of prefix stack.
+ *
+ * @param name The attribute qualified name (<code>name.value</code> is a
+ * <code>String</code> object which represents the attribute prefix).
+ * @param value The attribute value.
+ * @return <code>true</code> if a namespace declaration is recognized.
+ */
+ private boolean isdecl(Pair name, String value) {
+ if (name.chars[0] == 0) {
+ if ("xmlns".equals(name.name) == true) {
+ // New default namespace declaration
+ mPref = pair(mPref);
+ mPref.list = mElm; // prefix owner element
+ mPref.value = value;
+ mPref.name = "";
+ mPref.chars = NONS;
+ mElm.num++; // namespace counter
+ return true;
+ }
+ } else {
+ if (name.eqpref(XMLNS) == true) {
+ // New prefix declaration
+ int len = name.name.length();
+ mPref = pair(mPref);
+ mPref.list = mElm; // prefix owner element
+ mPref.value = value;
+ mPref.name = name.name;
+ mPref.chars = new char[len + 1];
+ mPref.chars[0] = (char) (len + 1);
+ name.name.getChars(0, len, mPref.chars, 1);
+ mElm.num++; // namespace counter
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Resolves a prefix.
+ *
+ * @return The namespace assigned to the prefix.
+ * @exception Exception When mapping for specified prefix is not found.
+ */
+ private String rslv(char[] qname)
+ throws Exception {
+ for (Pair pref = mPref; pref != null; pref = pref.next) {
+ if (pref.eqpref(qname) == true) {
+ return pref.value;
+ }
+ }
+ if (qname[0] == 1) { // QNames like ':local'
+ for (Pair pref = mPref; pref != null; pref = pref.next) {
+ if (pref.chars[0] == 0) {
+ return pref.value;
+ }
+ }
+ }
+ panic(FAULT);
+ return null;
+ }
+
+ /**
+ * Skips xml white space characters.
+ *
+ * This method skips white space characters (' ', '\t', '\n', '\r') and
+ * looks ahead not white space character.
+ *
+ * @return The first not white space look ahead character.
+ * @exception IOException
+ */
+ protected char wsskip()
+ throws IOException {
+ char ch;
+ while (true) {
+ // Read next character
+ ch = (mChIdx < mChLen) ? mChars[mChIdx++] : getch();
+ if (ch < 0x80) {
+ if (nmttyp[ch] != 3) // [ \t\n\r]
+ {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ mChIdx--; // bkch();
+ return ch;
+ }
+
+ /**
+ * Reports document type.
+ *
+ * @param name The name of the entity.
+ * @param pubid The public identifier of the entity or <code>null</code>.
+ * @param sysid The system identifier of the entity or <code>null</code>.
+ */
+ protected abstract void docType(String name, String pubid, String sysid)
+ throws SAXException;
+
+ /**
+ * Reports a comment.
+ *
+ * @param text The comment text starting from first charcater.
+ * @param length The number of characters in comment.
+ */
+ protected abstract void comm(char[] text, int length);
+
+ /**
+ * Reports a processing instruction.
+ *
+ * @param target The processing instruction target name.
+ * @param body The processing instruction body text.
+ */
+ protected abstract void pi(String target, String body)
+ throws Exception;
+
+ /**
+ * Reports new namespace prefix. The Namespace prefix (
+ * <code>mPref.name</code>) being declared and the Namespace URI (
+ * <code>mPref.value</code>) the prefix is mapped to. An empty string is
+ * used for the default element namespace, which has no prefix.
+ */
+ protected abstract void newPrefix()
+ throws Exception;
+
+ /**
+ * Reports skipped entity name.
+ *
+ * @param name The entity name.
+ */
+ protected abstract void skippedEnt(String name)
+ throws Exception;
+
+ /**
+ * Returns an
+ * <code>InputSource</code> for specified entity or
+ * <code>null</code>.
+ *
+ * @param name The name of the entity.
+ * @param pubid The public identifier of the entity.
+ * @param sysid The system identifier of the entity.
+ */
+ protected abstract InputSource resolveEnt(
+ String name, String pubid, String sysid)
+ throws Exception;
+
+ /**
+ * Reports notation declaration.
+ *
+ * @param name The notation's name.
+ * @param pubid The notation's public identifier, or null if none was given.
+ * @param sysid The notation's system identifier, or null if none was given.
+ */
+ protected abstract void notDecl(String name, String pubid, String sysid)
+ throws Exception;
+
+ /**
+ * Reports unparsed entity name.
+ *
+ * @param name The unparsed entity's name.
+ * @param pubid The entity's public identifier, or null if none was given.
+ * @param sysid The entity's system identifier.
+ * @param notation The name of the associated notation.
+ */
+ protected abstract void unparsedEntDecl(
+ String name, String pubid, String sysid, String notation)
+ throws Exception;
+
+ /**
+ * Notifies the handler about fatal parsing error.
+ *
+ * @param msg The problem description message.
+ */
+ protected abstract void panic(String msg)
+ throws Exception;
+
+ /**
+ * Reads a qualified xml name.
+ *
+ * This is low level routine which leaves a qName in the buffer. The
+ * characters of a qualified name is an array of characters. The first
+ * (chars[0]) character is the index of the colon character which separates
+ * the prefix from the local name. If the index is zero, the name does not
+ * contain separator or the parser works in the namespace unaware mode. The
+ * length of qualified name is the length of the array minus one.
+ *
+ * @param ns The true value turns namespace conformance on.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void bname(boolean ns)
+ throws Exception {
+ char ch;
+ char type;
+ mBuffIdx++; // allocate a char for colon offset
+ int bqname = mBuffIdx;
+ int bcolon = bqname;
+ int bchidx = bqname + 1;
+ int bstart = bchidx;
+ int cstart = mChIdx;
+ short st = (short) ((ns == true) ? 0 : 2);
+ while (true) {
+ // Read next character
+ if (mChIdx >= mChLen) {
+ bcopy(cstart, bstart);
+ getch();
+ mChIdx--; // bkch();
+ cstart = mChIdx;
+ bstart = bchidx;
+ }
+ ch = mChars[mChIdx++];
+ type = (char) 0; // [X]
+ if (ch < 0x80) {
+ type = (char) nmttyp[ch];
+ } else if (ch == EOS) {
+ panic(FAULT);
+ }
+ // Parse QName
+ switch (st) {
+ case 0: // read the first char of the prefix
+ case 2: // read the first char of the suffix
+ switch (type) {
+ case 0: // [aA_X]
+ bchidx++; // append char to the buffer
+ st++; // (st == 0)? 1: 3;
+ break;
+
+ case 1: // [:]
+ mChIdx--; // bkch();
+ st++; // (st == 0)? 1: 3;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 1: // read the prefix
+ case 3: // read the suffix
+ switch (type) {
+ case 0: // [aA_X]
+ case 2: // [.-d]
+ bchidx++; // append char to the buffer
+ break;
+
+ case 1: // [:]
+ bchidx++; // append char to the buffer
+ if (ns == true) {
+ if (bcolon != bqname) {
+ panic(FAULT); // it must be only one colon
+ }
+ bcolon = bchidx - 1;
+ if (st == 1) {
+ st = 2;
+ }
+ }
+ break;
+
+ default:
+ mChIdx--; // bkch();
+ bcopy(cstart, bstart);
+ mBuff[bqname] = (char) (bcolon - bqname);
+ return;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Reads a nmtoken.
+ *
+ * This is low level routine which leaves a nmtoken in the buffer.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void bntok() throws Exception {
+ char ch;
+ mBuffIdx = -1;
+ bappend((char) 0); // default offset to the colon char
+ while (true) {
+ ch = getch();
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case 'd':
+ case '.':
+ case ':':
+ case '-':
+ case '_':
+ case 'X':
+ bappend(ch);
+ break;
+
+ case 'Z':
+ panic(FAULT);
+
+ default:
+ bkch();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Recognizes a keyword.
+ *
+ * This is low level routine which recognizes one of keywords in the buffer.
+ * Keyword Id ID - i IDREF - r IDREFS - R ENTITY - n ENTITIES - N NMTOKEN -
+ * t NMTOKENS - T ELEMENT - e ATTLIST - a NOTATION - o CDATA - c REQUIRED -
+ * Q IMPLIED - I FIXED - F
+ *
+ * @return an id of a keyword or '?'.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private char bkeyword()
+ throws Exception {
+ String str = new String(mBuff, 1, mBuffIdx);
+ switch (str.length()) {
+ case 2: // ID
+ return ("ID".equals(str) == true) ? 'i' : '?';
+
+ case 5: // IDREF, CDATA, FIXED
+ switch (mBuff[1]) {
+ case 'I':
+ return ("IDREF".equals(str) == true) ? 'r' : '?';
+ case 'C':
+ return ("CDATA".equals(str) == true) ? 'c' : '?';
+ case 'F':
+ return ("FIXED".equals(str) == true) ? 'F' : '?';
+ default:
+ break;
+ }
+ break;
+
+ case 6: // IDREFS, ENTITY
+ switch (mBuff[1]) {
+ case 'I':
+ return ("IDREFS".equals(str) == true) ? 'R' : '?';
+ case 'E':
+ return ("ENTITY".equals(str) == true) ? 'n' : '?';
+ default:
+ break;
+ }
+ break;
+
+ case 7: // NMTOKEN, IMPLIED, ATTLIST, ELEMENT
+ switch (mBuff[1]) {
+ case 'I':
+ return ("IMPLIED".equals(str) == true) ? 'I' : '?';
+ case 'N':
+ return ("NMTOKEN".equals(str) == true) ? 't' : '?';
+ case 'A':
+ return ("ATTLIST".equals(str) == true) ? 'a' : '?';
+ case 'E':
+ return ("ELEMENT".equals(str) == true) ? 'e' : '?';
+ default:
+ break;
+ }
+ break;
+
+ case 8: // ENTITIES, NMTOKENS, NOTATION, REQUIRED
+ switch (mBuff[2]) {
+ case 'N':
+ return ("ENTITIES".equals(str) == true) ? 'N' : '?';
+ case 'M':
+ return ("NMTOKENS".equals(str) == true) ? 'T' : '?';
+ case 'O':
+ return ("NOTATION".equals(str) == true) ? 'o' : '?';
+ case 'E':
+ return ("REQUIRED".equals(str) == true) ? 'Q' : '?';
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return '?';
+ }
+
+ /**
+ * Reads a single or double quotted string in to the buffer.
+ *
+ * This method resolves entities inside a string unless the parser parses
+ * DTD.
+ *
+ * @param flag 'c' - CDATA, 'i' - non CDATA, ' ' - no normalization; '-' -
+ * not an attribute value; 'd' - in DTD context.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void bqstr(char flag) throws Exception {
+ Input inp = mInp; // remember the original input
+ mBuffIdx = -1;
+ bappend((char) 0); // default offset to the colon char
+ char ch;
+ for (short st = 0; st >= 0;) {
+ ch = (mChIdx < mChLen) ? mChars[mChIdx++] : getch();
+ switch (st) {
+ case 0: // read a single or double quote
+ switch (ch) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ break;
+
+ case '\'':
+ st = 2; // read a single quoted string
+ break;
+
+ case '\"':
+ st = 3; // read a double quoted string
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case 2: // read a single quoted string
+ case 3: // read a double quoted string
+ switch (ch) {
+ case '\'':
+ if ((st == 2) && (mInp == inp)) {
+ st = -1;
+ } else {
+ bappend(ch);
+ }
+ break;
+
+ case '\"':
+ if ((st == 3) && (mInp == inp)) {
+ st = -1;
+ } else {
+ bappend(ch);
+ }
+ break;
+
+ case '&':
+ if (flag != 'd') {
+ ent(flag);
+ } else {
+ bappend(ch);
+ }
+ break;
+
+ case '%':
+ if (flag == 'd') {
+ pent('-');
+ } else {
+ bappend(ch);
+ }
+ break;
+
+ case '<':
+ if ((flag == '-') || (flag == 'd')) {
+ bappend(ch);
+ } else {
+ panic(FAULT);
+ }
+ break;
+
+ case EOS: // EOS before single/double quote
+ panic(FAULT);
+
+ case '\r': // EOL processing [#2.11 & #3.3.3]
+ if (flag != ' ' && mInp.next == null) {
+ if (getch() != '\n') {
+ bkch();
+ }
+ ch = '\n';
+ }
+ default:
+ bappend(ch, flag);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ // There is maximum one space at the end of the string in
+ // i-mode (non CDATA normalization) and it has to be removed.
+ if ((flag == 'i') && (mBuff[mBuffIdx] == ' ')) {
+ mBuffIdx -= 1;
+ }
+ }
+
+ /**
+ * Reports characters and empties the parser's buffer. This method is called
+ * only if parser is going to return control to the main loop. This means
+ * that this method may use parser buffer to report white space without
+ * copeing characters to temporary buffer.
+ */
+ protected abstract void bflash()
+ throws Exception;
+
+ /**
+ * Reports white space characters and empties the parser's buffer. This
+ * method is called only if parser is going to return control to the main
+ * loop. This means that this method may use parser buffer to report white
+ * space without copeing characters to temporary buffer.
+ */
+ protected abstract void bflash_ws()
+ throws Exception;
+
+ /**
+ * Appends a character to parser's buffer with normalization.
+ *
+ * @param ch The character to append to the buffer.
+ * @param mode The normalization mode.
+ */
+ private void bappend(char ch, char mode) {
+ // This implements attribute value normalization as
+ // described in the XML specification [#3.3.3].
+ switch (mode) {
+ case 'i': // non CDATA normalization
+ switch (ch) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ if ((mBuffIdx > 0) && (mBuff[mBuffIdx] != ' ')) {
+ bappend(' ');
+ }
+ return;
+
+ default:
+ break;
+ }
+ break;
+
+ case 'c': // CDATA normalization
+ switch (ch) {
+ case '\n':
+ case '\r':
+ case '\t':
+ ch = ' ';
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default: // no normalization
+ break;
+ }
+ mBuffIdx++;
+ if (mBuffIdx < mBuff.length) {
+ mBuff[mBuffIdx] = ch;
+ } else {
+ mBuffIdx--;
+ bappend(ch);
+ }
+ }
+
+ /**
+ * Appends a character to parser's buffer.
+ *
+ * @param ch The character to append to the buffer.
+ */
+ private void bappend(char ch) {
+ try {
+ mBuff[++mBuffIdx] = ch;
+ } catch (Exception exp) {
+ // Double the buffer size
+ char buff[] = new char[mBuff.length << 1];
+ System.arraycopy(mBuff, 0, buff, 0, mBuff.length);
+ mBuff = buff;
+ mBuff[mBuffIdx] = ch;
+ }
+ }
+
+ /**
+ * Appends (mChIdx - cidx) characters from character buffer (mChars) to
+ * parser's buffer (mBuff).
+ *
+ * @param cidx The character buffer (mChars) start index.
+ * @param bidx The parser buffer (mBuff) start index.
+ */
+ private void bcopy(int cidx, int bidx) {
+ int length = mChIdx - cidx;
+ if ((bidx + length + 1) >= mBuff.length) {
+ // Expand the buffer
+ char buff[] = new char[mBuff.length + length];
+ System.arraycopy(mBuff, 0, buff, 0, mBuff.length);
+ mBuff = buff;
+ }
+ System.arraycopy(mChars, cidx, mBuff, bidx, length);
+ mBuffIdx += length;
+ }
+
+ /**
+ * Recognizes the built-in entities <i>lt</i>, <i>gt</i>, <i>amp</i>,
+ * <i>apos</i>, <i>quot</i>. The initial state is 0x100. Any state belowe
+ * 0x100 is a built-in entity replacement character.
+ *
+ * @param ch the next character of an entity name.
+ */
+ @SuppressWarnings("fallthrough")
+ private void eappend(char ch) {
+ switch (mESt) {
+ case 0x100: // "l" or "g" or "a" or "q"
+ switch (ch) {
+ case 'l':
+ mESt = 0x101;
+ break;
+ case 'g':
+ mESt = 0x102;
+ break;
+ case 'a':
+ mESt = 0x103;
+ break;
+ case 'q':
+ mESt = 0x107;
+ break;
+ default:
+ mESt = 0x200;
+ break;
+ }
+ break;
+
+ case 0x101: // "lt"
+ mESt = (ch == 't') ? '<' : (char) 0x200;
+ break;
+
+ case 0x102: // "gt"
+ mESt = (ch == 't') ? '>' : (char) 0x200;
+ break;
+
+ case 0x103: // "am" or "ap"
+ switch (ch) {
+ case 'm':
+ mESt = 0x104;
+ break;
+ case 'p':
+ mESt = 0x105;
+ break;
+ default:
+ mESt = 0x200;
+ break;
+ }
+ break;
+
+ case 0x104: // "amp"
+ mESt = (ch == 'p') ? '&' : (char) 0x200;
+ break;
+
+ case 0x105: // "apo"
+ mESt = (ch == 'o') ? (char) 0x106 : (char) 0x200;
+ break;
+
+ case 0x106: // "apos"
+ mESt = (ch == 's') ? '\'' : (char) 0x200;
+ break;
+
+ case 0x107: // "qu"
+ mESt = (ch == 'u') ? (char) 0x108 : (char) 0x200;
+ break;
+
+ case 0x108: // "quo"
+ mESt = (ch == 'o') ? (char) 0x109 : (char) 0x200;
+ break;
+
+ case 0x109: // "quot"
+ mESt = (ch == 't') ? '\"' : (char) 0x200;
+ break;
+
+ case '<': // "lt"
+ case '>': // "gt"
+ case '&': // "amp"
+ case '\'': // "apos"
+ case '\"': // "quot"
+ mESt = 0x200;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Sets up a new input source on the top of the input stack. Note, the first
+ * byte returned by the entity's byte stream has to be the first byte in the
+ * entity. However, the parser does not expect the byte order mask in both
+ * cases when encoding is provided by the input source.
+ *
+ * @param is A new input source to set up.
+ * @exception IOException If any IO errors occur.
+ * @exception Exception is parser specific exception form panic method.
+ */
+ protected void setinp(InputSource is)
+ throws Exception {
+ Reader reader = null;
+ mChIdx = 0;
+ mChLen = 0;
+ mChars = mInp.chars;
+ mInp.src = null;
+ if (mPh < PH_DOC_START) {
+ mIsSAlone = false; // default [#2.9]
+ }
+ mIsSAloneSet = false;
+ if (is.getCharacterStream() != null) {
+ // Ignore encoding in the xml text decl.
+ reader = is.getCharacterStream();
+ xml(reader);
+ } else if (is.getByteStream() != null) {
+ String expenc;
+ if (is.getEncoding() != null) {
+ // Ignore encoding in the xml text decl.
+ expenc = is.getEncoding().toUpperCase();
+ if (expenc.equals("UTF-16")) {
+ reader = bom(is.getByteStream(), 'U'); // UTF-16 [#4.3.3]
+ } else {
+ reader = enc(expenc, is.getByteStream());
+ }
+ xml(reader);
+ } else {
+ // Get encoding from BOM or the xml text decl.
+ reader = bom(is.getByteStream(), ' ');
+ if (reader == null) {
+ // Encoding is defined by the xml text decl.
+ reader = enc("UTF-8", is.getByteStream());
+ expenc = xml(reader);
+ if (expenc.startsWith("UTF-16")) {
+ panic(FAULT); // UTF-16 must have BOM [#4.3.3]
+ }
+ reader = enc(expenc, is.getByteStream());
+ } else {
+ // Encoding is defined by the BOM.
+ xml(reader);
+ }
+ }
+ } else {
+ // There is no support for public/system identifiers.
+ panic(FAULT);
+ }
+ mInp.src = reader;
+ mInp.pubid = is.getPublicId();
+ mInp.sysid = is.getSystemId();
+ }
+
+ /**
+ * Determines the entity encoding.
+ *
+ * This method gets encoding from Byte Order Mask [#4.3.3] if any. Note, the
+ * first byte returned by the entity's byte stream has to be the first byte
+ * in the entity. Also, there is no support for UCS-4.
+ *
+ * @param is A byte stream of the entity.
+ * @param hint An encoding hint, character U means UTF-16.
+ * @return a reader constructed from the BOM or UTF-8 by default.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private Reader bom(InputStream is, char hint)
+ throws Exception {
+ int val = is.read();
+ switch (val) {
+ case 0xef: // UTF-8
+ if (hint == 'U') // must be UTF-16
+ {
+ panic(FAULT);
+ }
+ if (is.read() != 0xbb) {
+ panic(FAULT);
+ }
+ if (is.read() != 0xbf) {
+ panic(FAULT);
+ }
+ return new ReaderUTF8(is);
+
+ case 0xfe: // UTF-16, big-endian
+ if (is.read() != 0xff) {
+ panic(FAULT);
+ }
+ return new ReaderUTF16(is, 'b');
+
+ case 0xff: // UTF-16, little-endian
+ if (is.read() != 0xfe) {
+ panic(FAULT);
+ }
+ return new ReaderUTF16(is, 'l');
+
+ case -1:
+ mChars[mChIdx++] = EOS;
+ return new ReaderUTF8(is);
+
+ default:
+ if (hint == 'U') // must be UTF-16
+ {
+ panic(FAULT);
+ }
+ // Read the rest of UTF-8 character
+ switch (val & 0xf0) {
+ case 0xc0:
+ case 0xd0:
+ mChars[mChIdx++] = (char) (((val & 0x1f) << 6) | (is.read() & 0x3f));
+ break;
+
+ case 0xe0:
+ mChars[mChIdx++] = (char) (((val & 0x0f) << 12)
+ | ((is.read() & 0x3f) << 6) | (is.read() & 0x3f));
+ break;
+
+ case 0xf0: // UCS-4 character
+ throw new UnsupportedEncodingException();
+
+ default:
+ mChars[mChIdx++] = (char) val;
+ break;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Parses the xml text declaration.
+ *
+ * This method gets encoding from the xml text declaration [#4.3.1] if any.
+ * The method assumes the buffer (mChars) is big enough to accomodate whole
+ * xml text declaration.
+ *
+ * @param reader is entity reader.
+ * @return The xml text declaration encoding or default UTF-8 encoding.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private String xml(Reader reader)
+ throws Exception {
+ String str = null;
+ String enc = "UTF-8";
+ char ch;
+ int val;
+ short st;
+ // Read the xml text declaration into the buffer
+ if (mChIdx != 0) {
+ // The bom method have read ONE char into the buffer.
+ st = (short) ((mChars[0] == '<') ? 1 : -1);
+ } else {
+ st = 0;
+ }
+ while (st >= 0 && mChIdx < mChars.length) {
+ ch = ((val = reader.read()) >= 0) ? (char) val : EOS;
+ mChars[mChIdx++] = ch;
+ switch (st) {
+ case 0: // read '<' of xml declaration
+ switch (ch) {
+ case '<':
+ st = 1;
+ break;
+
+ case 0xfeff: // the byte order mask
+ ch = ((val = reader.read()) >= 0) ? (char) val : EOS;
+ mChars[mChIdx - 1] = ch;
+ st = (short) ((ch == '<') ? 1 : -1);
+ break;
+
+ default:
+ st = -1;
+ break;
+ }
+ break;
+
+ case 1: // read '?' of xml declaration [#4.3.1]
+ st = (short) ((ch == '?') ? 2 : -1);
+ break;
+
+ case 2: // read 'x' of xml declaration [#4.3.1]
+ st = (short) ((ch == 'x') ? 3 : -1);
+ break;
+
+ case 3: // read 'm' of xml declaration [#4.3.1]
+ st = (short) ((ch == 'm') ? 4 : -1);
+ break;
+
+ case 4: // read 'l' of xml declaration [#4.3.1]
+ st = (short) ((ch == 'l') ? 5 : -1);
+ break;
+
+ case 5: // read white space after 'xml'
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ st = 6;
+ break;
+
+ default:
+ st = -1;
+ break;
+ }
+ break;
+
+ case 6: // read content of xml declaration
+ switch (ch) {
+ case '?':
+ st = 7;
+ break;
+
+ case EOS:
+ st = -2;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 7: // read '>' after '?' of xml declaration
+ switch (ch) {
+ case '>':
+ case EOS:
+ st = -2;
+ break;
+
+ default:
+ st = 6;
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ }
+ mChLen = mChIdx;
+ mChIdx = 0;
+ // If there is no xml text declaration, the encoding is default.
+ if (st == -1) {
+ return enc;
+ }
+ mChIdx = 5; // the first white space after "<?xml"
+ // Parse the xml text declaration
+ for (st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // skip spaces after the xml declaration name
+ if (chtyp(ch) != ' ') {
+ bkch();
+ st = 1;
+ }
+ break;
+
+ case 1: // read xml declaration version
+ case 2: // read xml declaration encoding or standalone
+ case 3: // read xml declaration standalone
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case '_':
+ bkch();
+ str = name(false).toLowerCase();
+ if ("version".equals(str) == true) {
+ if (st != 1) {
+ panic(FAULT);
+ }
+ if ("1.0".equals(eqstr('=')) != true) {
+ panic(FAULT);
+ }
+ mInp.xmlver = 0x0100;
+ st = 2;
+ } else if ("encoding".equals(str) == true) {
+ if (st != 2) {
+ panic(FAULT);
+ }
+ mInp.xmlenc = eqstr('=').toUpperCase();
+ enc = mInp.xmlenc;
+ st = 3;
+ } else if ("standalone".equals(str) == true) {
+ if ((st == 1) || (mPh >= PH_DOC_START)) // [#4.3.1]
+ {
+ panic(FAULT);
+ }
+ str = eqstr('=').toLowerCase();
+ // Check the 'standalone' value and use it [#5.1]
+ if (str.equals("yes") == true) {
+ mIsSAlone = true;
+ } else if (str.equals("no") == true) {
+ mIsSAlone = false;
+ } else {
+ panic(FAULT);
+ }
+ mIsSAloneSet = true;
+ st = 4;
+ } else {
+ panic(FAULT);
+ }
+ break;
+
+ case ' ':
+ break;
+
+ case '?':
+ if (st == 1) {
+ panic(FAULT);
+ }
+ bkch();
+ st = 4;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 4: // end of xml declaration
+ switch (chtyp(ch)) {
+ case '?':
+ if (getch() != '>') {
+ panic(FAULT);
+ }
+ if (mPh <= PH_DOC_START) {
+ mPh = PH_MISC_DTD; // misc before DTD
+ }
+ st = -1;
+ break;
+
+ case ' ':
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ return enc;
+ }
+
+ /**
+ * Sets up the document reader.
+ *
+ * @param name an encoding name.
+ * @param is the document byte input stream.
+ * @return a reader constructed from encoding name and input stream.
+ * @exception UnsupportedEncodingException
+ */
+ private Reader enc(String name, InputStream is)
+ throws UnsupportedEncodingException {
+ // DO NOT CLOSE current reader if any!
+ if (name.equals("UTF-8")) {
+ return new ReaderUTF8(is);
+ } else if (name.equals("UTF-16LE")) {
+ return new ReaderUTF16(is, 'l');
+ } else if (name.equals("UTF-16BE")) {
+ return new ReaderUTF16(is, 'b');
+ } else {
+ return new InputStreamReader(is, name);
+ }
+ }
+
+ /**
+ * Sets up current input on the top of the input stack.
+ *
+ * @param inp A new input to set up.
+ */
+ protected void push(Input inp) {
+ mInp.chLen = mChLen;
+ mInp.chIdx = mChIdx;
+ inp.next = mInp;
+ mInp = inp;
+ mChars = inp.chars;
+ mChLen = inp.chLen;
+ mChIdx = inp.chIdx;
+ }
+
+ /**
+ * Restores previous input on the top of the input stack.
+ */
+ protected void pop() {
+ if (mInp.src != null) {
+ try {
+ mInp.src.close();
+ } catch (IOException ioe) {
+ }
+ mInp.src = null;
+ }
+ mInp = mInp.next;
+ if (mInp != null) {
+ mChars = mInp.chars;
+ mChLen = mInp.chLen;
+ mChIdx = mInp.chIdx;
+ } else {
+ mChars = null;
+ mChLen = 0;
+ mChIdx = 0;
+ }
+ }
+
+ /**
+ * Maps a character to it's type.
+ *
+ * Possible character type values are:<br /> - ' ' for any kind of white
+ * space character;<br /> - 'a' for any lower case alphabetical character
+ * value;<br /> - 'A' for any upper case alphabetical character value;<br />
+ * - 'd' for any decimal digit character value;<br /> - 'z' for any
+ * character less then ' ' except '\t', '\n', '\r';<br /> - 'X' for any not
+ * ASCII character;<br /> - 'Z' for EOS character.<br /> An ASCII (7 bit)
+ * character which does not fall in any category listed above is mapped to
+ * it self.
+ *
+ * @param ch The character to map.
+ * @return The type of character.
+ */
+ protected char chtyp(char ch) {
+ if (ch < 0x80) {
+ return (char) asctyp[ch];
+ }
+ return (ch != EOS) ? 'X' : 'Z';
+ }
+
+ /**
+ * Retrives the next character in the document.
+ *
+ * @return The next character in the document.
+ */
+ protected char getch()
+ throws IOException {
+ if (mChIdx >= mChLen) {
+ if (mInp.src == null) {
+ pop(); // remove internal entity
+ return getch();
+ }
+ // Read new portion of the document characters
+ int Num = mInp.src.read(mChars, 0, mChars.length);
+ if (Num < 0) {
+ if (mInp != mDoc) {
+ pop(); // restore the previous input
+ return getch();
+ } else {
+ mChars[0] = EOS;
+ mChLen = 1;
+ }
+ } else {
+ mChLen = Num;
+ }
+ mChIdx = 0;
+ }
+ return mChars[mChIdx++];
+ }
+
+ /**
+ * Puts back the last read character.
+ *
+ * This method <strong>MUST NOT</strong> be called more then once after each
+ * call of {@link #getch getch} method.
+ */
+ protected void bkch()
+ throws Exception {
+ if (mChIdx <= 0) {
+ panic(FAULT);
+ }
+ mChIdx--;
+ }
+
+ /**
+ * Sets the current character.
+ *
+ * @param ch The character to set.
+ */
+ protected void setch(char ch) {
+ mChars[mChIdx] = ch;
+ }
+
+ /**
+ * Finds a pair in the pair chain by a qualified name.
+ *
+ * @param chain The first element of the chain of pairs.
+ * @param qname The qualified name.
+ * @return A pair with the specified qualified name or null.
+ */
+ protected Pair find(Pair chain, char[] qname) {
+ for (Pair pair = chain; pair != null; pair = pair.next) {
+ if (pair.eqname(qname) == true) {
+ return pair;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Provedes an instance of a pair.
+ *
+ * @param next The reference to a next pair.
+ * @return An instance of a pair.
+ */
+ protected Pair pair(Pair next) {
+ Pair pair;
+
+ if (mDltd != null) {
+ pair = mDltd;
+ mDltd = pair.next;
+ } else {
+ pair = new Pair();
+ }
+ pair.next = next;
+
+ return pair;
+ }
+
+ /**
+ * Deletes an instance of a pair.
+ *
+ * @param pair The pair to delete.
+ * @return A reference to the next pair in a chain.
+ */
+ protected Pair del(Pair pair) {
+ Pair next = pair.next;
+
+ pair.name = null;
+ pair.value = null;
+ pair.chars = null;
+ pair.list = null;
+ pair.next = mDltd;
+ mDltd = pair;
+
+ return next;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/ParserSAX.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,695 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import jdk.internal.org.xml.sax.ContentHandler;
+import jdk.internal.org.xml.sax.DTDHandler;
+import jdk.internal.org.xml.sax.EntityResolver;
+import jdk.internal.org.xml.sax.ErrorHandler;
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.Locator;
+import jdk.internal.org.xml.sax.SAXException;
+import jdk.internal.org.xml.sax.SAXParseException;
+import jdk.internal.org.xml.sax.XMLReader;
+import jdk.internal.org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * XML non-validating push parser.
+ *
+ * This non-validating parser conforms to <a href="http://www.w3.org/TR/REC-xml"
+ * >Extensible Markup Language (XML) 1.0</a> and <a
+ * href="http://www.w3.org/TR/REC-xml-names" >"Namespaces in XML"</a>
+ * specifications. The API supported by the parser are <a
+ * href="http://java.sun.com/aboutJava/communityprocess/final/jsr030/index.html">CLDC
+ * 1.0</a> and <a href="http://www.jcp.org/en/jsr/detail?id=280">JSR-280</a>, a
+ * JavaME subset of <a href="http://java.sun.com/xml/jaxp/index.html">JAXP</a>
+ * and <a href="http://www.saxproject.org/">SAX2</a>.
+ *
+ * @see org.xml.sax.XMLReader
+ */
+
+final class ParserSAX
+ extends Parser implements XMLReader, Locator
+{
+ public final static String FEATURE_NS =
+ "http://xml.org/sax/features/namespaces";
+ public final static String FEATURE_PREF =
+ "http://xml.org/sax/features/namespace-prefixes";
+ // SAX feature flags
+ private boolean mFNamespaces;
+ private boolean mFPrefixes;
+ // SAX handlers
+ private DefaultHandler mHand; // the default handler
+ private ContentHandler mHandCont; // the content handler
+ private DTDHandler mHandDtd; // the DTD handler
+ private ErrorHandler mHandErr; // the error handler
+ private EntityResolver mHandEnt; // the entity resolver
+
+ /**
+ * Constructor.
+ */
+ public ParserSAX() {
+ super();
+
+ // SAX feature defaut values
+ mFNamespaces = true;
+ mFPrefixes = false;
+
+ // Default handler which will be used in case the application
+ // do not set one of handlers.
+ mHand = new DefaultHandler();
+ mHandCont = mHand;
+ mHandDtd = mHand;
+ mHandErr = mHand;
+ mHandEnt = mHand;
+ }
+
+ /**
+ * Return the current content handler.
+ *
+ * @return The current content handler, or null if none has been registered.
+ * @see #setContentHandler
+ */
+ public ContentHandler getContentHandler() {
+ return (mHandCont != mHand) ? mHandCont : null;
+ }
+
+ /**
+ * Allow an application to register a content event handler.
+ *
+ * <p>If the application does not register a content handler, all content
+ * events reported by the SAX parser will be silently ignored.</p>
+ *
+ * <p>Applications may register a new or different handler in the middle of
+ * a parse, and the SAX parser must begin using the new handler
+ * immediately.</p>
+ *
+ * @param handler The content handler.
+ * @exception java.lang.NullPointerException If the handler argument is
+ * null.
+ * @see #getContentHandler
+ */
+ public void setContentHandler(ContentHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException();
+ }
+ mHandCont = handler;
+ }
+
+ /**
+ * Return the current DTD handler.
+ *
+ * @return The current DTD handler, or null if none has been registered.
+ * @see #setDTDHandler
+ */
+ public DTDHandler getDTDHandler() {
+ return (mHandDtd != mHand) ? mHandDtd : null;
+ }
+
+ /**
+ * Allow an application to register a DTD event handler.
+ *
+ * <p>If the application does not register a DTD handler, all DTD events
+ * reported by the SAX parser will be silently ignored.</p>
+ *
+ * <p>Applications may register a new or different handler in the middle of
+ * a parse, and the SAX parser must begin using the new handler
+ * immediately.</p>
+ *
+ * @param handler The DTD handler.
+ * @exception java.lang.NullPointerException If the handler argument is
+ * null.
+ * @see #getDTDHandler
+ */
+ public void setDTDHandler(DTDHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException();
+ }
+ mHandDtd = handler;
+ }
+
+ /**
+ * Return the current error handler.
+ *
+ * @return The current error handler, or null if none has been registered.
+ * @see #setErrorHandler
+ */
+ public ErrorHandler getErrorHandler() {
+ return (mHandErr != mHand) ? mHandErr : null;
+ }
+
+ /**
+ * Allow an application to register an error event handler.
+ *
+ * <p>If the application does not register an error handler, all error
+ * events reported by the SAX parser will be silently ignored; however,
+ * normal processing may not continue. It is highly recommended that all SAX
+ * applications implement an error handler to avoid unexpected bugs.</p>
+ *
+ * <p>Applications may register a new or different handler in the middle of
+ * a parse, and the SAX parser must begin using the new handler
+ * immediately.</p>
+ *
+ * @param handler The error handler.
+ * @exception java.lang.NullPointerException If the handler argument is
+ * null.
+ * @see #getErrorHandler
+ */
+ public void setErrorHandler(ErrorHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException();
+ }
+ mHandErr = handler;
+ }
+
+ /**
+ * Return the current entity resolver.
+ *
+ * @return The current entity resolver, or null if none has been registered.
+ * @see #setEntityResolver
+ */
+ public EntityResolver getEntityResolver() {
+ return (mHandEnt != mHand) ? mHandEnt : null;
+ }
+
+ /**
+ * Allow an application to register an entity resolver.
+ *
+ * <p>If the application does not register an entity resolver, the XMLReader
+ * will perform its own default resolution.</p>
+ *
+ * <p>Applications may register a new or different resolver in the middle of
+ * a parse, and the SAX parser must begin using the new resolver
+ * immediately.</p>
+ *
+ * @param resolver The entity resolver.
+ * @exception java.lang.NullPointerException If the resolver argument is
+ * null.
+ * @see #getEntityResolver
+ */
+ public void setEntityResolver(EntityResolver resolver) {
+ if (resolver == null) {
+ throw new NullPointerException();
+ }
+ mHandEnt = resolver;
+ }
+
+ /**
+ * Return the public identifier for the current document event.
+ *
+ * <p>The return value is the public identifier of the document entity or of
+ * the external parsed entity in which the markup triggering the event
+ * appears.</p>
+ *
+ * @return A string containing the public identifier, or null if none is
+ * available.
+ *
+ * @see #getSystemId
+ */
+ public String getPublicId() {
+ return (mInp != null) ? mInp.pubid : null;
+ }
+
+ /**
+ * Return the system identifier for the current document event.
+ *
+ * <p>The return value is the system identifier of the document entity or of
+ * the external parsed entity in which the markup triggering the event
+ * appears.</p>
+ *
+ * <p>If the system identifier is a URL, the parser must resolve it fully
+ * before passing it to the application.</p>
+ *
+ * @return A string containing the system identifier, or null if none is
+ * available.
+ *
+ * @see #getPublicId
+ */
+ public String getSystemId() {
+ return (mInp != null) ? mInp.sysid : null;
+ }
+
+ /**
+ * Return the line number where the current document event ends.
+ *
+ * @return Always returns -1 indicating the line number is not available.
+ *
+ * @see #getColumnNumber
+ */
+ public int getLineNumber() {
+ return -1;
+ }
+
+ /**
+ * Return the column number where the current document event ends.
+ *
+ * @return Always returns -1 indicating the column number is not available.
+ *
+ * @see #getLineNumber
+ */
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /**
+ * Parse an XML document from a system identifier (URI).
+ *
+ * <p>This method is a shortcut for the common case of reading a document
+ * from a system identifier. It is the exact equivalent of the
+ * following:</p>
+ *
+ * <pre>
+ * parse(new InputSource(systemId));
+ * </pre>
+ *
+ * <p>If the system identifier is a URL, it must be fully resolved by the
+ * application before it is passed to the parser.</p>
+ *
+ * @param systemId The system identifier (URI).
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly wrapping
+ * another exception.
+ * @exception java.io.IOException An IO exception from the parser, possibly
+ * from a byte stream or character stream supplied by the application.
+ * @see #parse(org.xml.sax.InputSource)
+ */
+ public void parse(String systemId) throws IOException, SAXException {
+ parse(new InputSource(systemId));
+ }
+
+ /**
+ * Parse an XML document.
+ *
+ * <p>The application can use this method to instruct the XML reader to
+ * begin parsing an XML document from any valid input source (a character
+ * stream, a byte stream, or a URI).</p>
+ *
+ * <p>Applications may not invoke this method while a parse is in progress
+ * (they should create a new XMLReader instead for each nested XML
+ * document). Once a parse is complete, an application may reuse the same
+ * XMLReader object, possibly with a different input source.</p>
+ *
+ * <p>During the parse, the XMLReader will provide information about the XML
+ * document through the registered event handlers.</p>
+ *
+ * <p>This method is synchronous: it will not return until parsing has
+ * ended. If a client application wants to terminate parsing early, it
+ * should throw an exception.</p>
+ *
+ * @param is The input source for the top-level of the XML document.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly wrapping
+ * another exception.
+ * @exception java.io.IOException An IO exception from the parser, possibly
+ * from a byte stream or character stream supplied by the application.
+ * @see org.xml.sax.InputSource
+ * @see #parse(java.lang.String)
+ * @see #setEntityResolver
+ * @see #setDTDHandler
+ * @see #setContentHandler
+ * @see #setErrorHandler
+ */
+ public void parse(InputSource is) throws IOException, SAXException {
+ if (is == null) {
+ throw new IllegalArgumentException("");
+ }
+ // Set up the document
+ mInp = new Input(BUFFSIZE_READER);
+ mPh = PH_BEFORE_DOC; // before parsing
+ try {
+ setinp(is);
+ } catch (SAXException saxe) {
+ throw saxe;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (RuntimeException rte) {
+ throw rte;
+ } catch (Exception e) {
+ panic(e.toString());
+ }
+ parse();
+ }
+
+ /**
+ * Parse the content of the given {@link java.io.InputStream} instance as
+ * XML using the specified {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param src InputStream containing the content to be parsed.
+ * @param handler The SAX DefaultHandler to use.
+ * @exception IOException If any IO errors occur.
+ * @exception IllegalArgumentException If the given InputStream or handler
+ * is null.
+ * @exception SAXException If the underlying parser throws a SAXException
+ * while parsing.
+ * @see org.xml.sax.helpers.DefaultHandler
+ */
+ public void parse(InputStream src, DefaultHandler handler)
+ throws SAXException, IOException {
+ if ((src == null) || (handler == null)) {
+ throw new IllegalArgumentException("");
+ }
+ parse(new InputSource(src), handler);
+ }
+
+ /**
+ * Parse the content given {@link org.xml.sax.InputSource} as XML using the
+ * specified {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param is The InputSource containing the content to be parsed.
+ * @param handler The SAX DefaultHandler to use.
+ * @exception IOException If any IO errors occur.
+ * @exception IllegalArgumentException If the InputSource or handler is
+ * null.
+ * @exception SAXException If the underlying parser throws a SAXException
+ * while parsing.
+ * @see org.xml.sax.helpers.DefaultHandler
+ */
+ public void parse(InputSource is, DefaultHandler handler)
+ throws SAXException, IOException
+ {
+ if ((is == null) || (handler == null)) {
+ throw new IllegalArgumentException("");
+ }
+ // Set up the handler
+ mHandCont = handler;
+ mHandDtd = handler;
+ mHandErr = handler;
+ mHandEnt = handler;
+ // Set up the document
+ mInp = new Input(BUFFSIZE_READER);
+ mPh = PH_BEFORE_DOC; // before parsing
+ try {
+ setinp(is);
+ } catch (SAXException | IOException | RuntimeException saxe) {
+ throw saxe;
+ } catch (Exception e) {
+ panic(e.toString());
+ }
+ parse();
+ }
+
+ /**
+ * Parse the XML document content using specified handlers and an input
+ * source.
+ *
+ * @exception IOException If any IO errors occur.
+ * @exception SAXException If the underlying parser throws a SAXException
+ * while parsing.
+ */
+ @SuppressWarnings("fallthrough")
+ private void parse() throws SAXException, IOException {
+ init();
+ try {
+ mHandCont.setDocumentLocator(this);
+ mHandCont.startDocument();
+
+ if (mPh != PH_MISC_DTD) {
+ mPh = PH_MISC_DTD; // misc before DTD
+ }
+ int evt = EV_NULL;
+ // XML document prolog
+ do {
+ wsskip();
+ switch (evt = step()) {
+ case EV_ELM:
+ case EV_ELMS:
+ mPh = PH_DOCELM;
+ break;
+
+ case EV_COMM:
+ case EV_PI:
+ break;
+
+ case EV_DTD:
+ if (mPh >= PH_DTD_MISC) {
+ panic(FAULT);
+ }
+ mPh = PH_DTD_MISC; // misc after DTD
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ } while (mPh < PH_DOCELM); // misc before DTD
+ // XML document starting with document's element
+ do {
+ switch (evt) {
+ case EV_ELM:
+ case EV_ELMS:
+ // Report the element
+ if (mIsNSAware == true) {
+ mHandCont.startElement(
+ mElm.value,
+ mElm.name,
+ "",
+ mAttrs);
+ } else {
+ mHandCont.startElement(
+ "",
+ "",
+ mElm.name,
+ mAttrs);
+ }
+ if (evt == EV_ELMS) {
+ evt = step();
+ break;
+ }
+
+ case EV_ELME:
+ // Report the end of element
+ if (mIsNSAware == true) {
+ mHandCont.endElement(mElm.value, mElm.name, "");
+ } else {
+ mHandCont.endElement("", "", mElm.name);
+ }
+ // Restore the top of the prefix stack
+ while (mPref.list == mElm) {
+ mHandCont.endPrefixMapping(mPref.name);
+ mPref = del(mPref);
+ }
+ // Remove the top element tag
+ mElm = del(mElm);
+ if (mElm == null) {
+ mPh = PH_DOCELM_MISC;
+ } else {
+ evt = step();
+ }
+ break;
+
+ case EV_TEXT:
+ case EV_WSPC:
+ case EV_CDAT:
+ case EV_COMM:
+ case EV_PI:
+ case EV_ENT:
+ evt = step();
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ } while (mPh == PH_DOCELM);
+ // Misc after document's element
+ do {
+ if (wsskip() == EOS) {
+ break;
+ }
+
+ switch (step()) {
+ case EV_COMM:
+ case EV_PI:
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ } while (mPh == PH_DOCELM_MISC);
+ mPh = PH_AFTER_DOC; // parsing is completed
+
+ } catch (SAXException saxe) {
+ throw saxe;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (RuntimeException rte) {
+ throw rte;
+ } catch (Exception e) {
+ panic(e.toString());
+ } finally {
+ mHandCont.endDocument();
+ cleanup();
+ }
+ }
+
+ /**
+ * Reports document type.
+ *
+ * @param name The name of the entity.
+ * @param pubid The public identifier of the entity or <code>null</code>.
+ * @param sysid The system identifier of the entity or <code>null</code>.
+ */
+ protected void docType(String name, String pubid, String sysid) throws SAXException {
+ mHandDtd.notationDecl(name, pubid, sysid);
+ }
+
+ /**
+ * Reports a comment.
+ *
+ * @param text The comment text starting from first charcater.
+ * @param length The number of characters in comment.
+ */
+ protected void comm(char[] text, int length) {
+ }
+
+ /**
+ * Reports a processing instruction.
+ *
+ * @param target The processing instruction target name.
+ * @param body The processing instruction body text.
+ */
+ protected void pi(String target, String body) throws SAXException {
+ mHandCont.processingInstruction(target, body);
+ }
+
+ /**
+ * Reports new namespace prefix. The Namespace prefix (
+ * <code>mPref.name</code>) being declared and the Namespace URI (
+ * <code>mPref.value</code>) the prefix is mapped to. An empty string is
+ * used for the default element namespace, which has no prefix.
+ */
+ protected void newPrefix() throws SAXException {
+ mHandCont.startPrefixMapping(mPref.name, mPref.value);
+ }
+
+ /**
+ * Reports skipped entity name.
+ *
+ * @param name The entity name.
+ */
+ protected void skippedEnt(String name) throws SAXException {
+ mHandCont.skippedEntity(name);
+ }
+
+ /**
+ * Returns an
+ * <code>InputSource</code> for specified entity or
+ * <code>null</code>.
+ *
+ * @param name The name of the entity.
+ * @param pubid The public identifier of the entity.
+ * @param sysid The system identifier of the entity.
+ */
+ protected InputSource resolveEnt(String name, String pubid, String sysid)
+ throws SAXException, IOException
+ {
+ return mHandEnt.resolveEntity(pubid, sysid);
+ }
+
+ /**
+ * Reports notation declaration.
+ *
+ * @param name The notation's name.
+ * @param pubid The notation's public identifier, or null if none was given.
+ * @param sysid The notation's system identifier, or null if none was given.
+ */
+ protected void notDecl(String name, String pubid, String sysid)
+ throws SAXException
+ {
+ mHandDtd.notationDecl(name, pubid, sysid);
+ }
+
+ /**
+ * Reports unparsed entity name.
+ *
+ * @param name The unparsed entity's name.
+ * @param pubid The entity's public identifier, or null if none was given.
+ * @param sysid The entity's system identifier.
+ * @param notation The name of the associated notation.
+ */
+ protected void unparsedEntDecl(String name, String pubid, String sysid, String notation)
+ throws SAXException
+ {
+ mHandDtd.unparsedEntityDecl(name, pubid, sysid, notation);
+ }
+
+ /**
+ * Notifies the handler about fatal parsing error.
+ *
+ * @param msg The problem description message.
+ */
+ protected void panic(String msg) throws SAXException {
+ SAXParseException spe = new SAXParseException(msg, this);
+ mHandErr.fatalError(spe);
+ throw spe; // [#1.2] fatal error definition
+ }
+
+ /**
+ * Reports characters and empties the parser's buffer. This method is called
+ * only if parser is going to return control to the main loop. This means
+ * that this method may use parser buffer to report white space without
+ * copeing characters to temporary buffer.
+ */
+ protected void bflash() throws SAXException {
+ if (mBuffIdx >= 0) {
+ // Textual data has been read
+ mHandCont.characters(mBuff, 0, (mBuffIdx + 1));
+ mBuffIdx = -1;
+ }
+ }
+
+ /**
+ * Reports white space characters and empties the parser's buffer. This
+ * method is called only if parser is going to return control to the main
+ * loop. This means that this method may use parser buffer to report white
+ * space without copeing characters to temporary buffer.
+ */
+ protected void bflash_ws() throws SAXException {
+ if (mBuffIdx >= 0) {
+ // BUG: With additional info from DTD and xml:space attr [#2.10]
+ // the following call can be supported:
+ // mHandCont.ignorableWhitespace(mBuff, 0, (mBuffIdx + 1));
+
+ // Textual data has been read
+ mHandCont.characters(mBuff, 0, (mBuffIdx + 1));
+ mBuffIdx = -1;
+ }
+ }
+
+ public boolean getFeature(String name) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void setFeature(String name, boolean value) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Object getProperty(String name) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void setProperty(String name, Object value) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/ReaderUTF16.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.Reader;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * UTF-16 encoded stream reader.
+ */
+public class ReaderUTF16 extends Reader {
+
+ private InputStream is;
+ private char bo;
+
+ /**
+ * Constructor.
+ *
+ * Byte order argument can be: 'l' for little-endian or 'b' for big-endian.
+ *
+ * @param is A byte input stream.
+ * @param bo A byte order in the input stream.
+ */
+ public ReaderUTF16(InputStream is, char bo) {
+ switch (bo) {
+ case 'l':
+ break;
+
+ case 'b':
+ break;
+
+ default:
+ throw new IllegalArgumentException("");
+ }
+ this.bo = bo;
+ this.is = is;
+ }
+
+ /**
+ * Reads characters into a portion of an array.
+ *
+ * @param cbuf Destination buffer.
+ * @param off Offset at which to start storing characters.
+ * @param len Maximum number of characters to read.
+ * @exception IOException If any IO errors occur.
+ */
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ int num = 0;
+ int val;
+ if (bo == 'b') {
+ while (num < len) {
+ if ((val = is.read()) < 0) {
+ return (num != 0) ? num : -1;
+ }
+ cbuf[off++] = (char) ((val << 8) | (is.read() & 0xff));
+ num++;
+ }
+ } else {
+ while (num < len) {
+ if ((val = is.read()) < 0) {
+ return (num != 0) ? num : -1;
+ }
+ cbuf[off++] = (char) ((is.read() << 8) | (val & 0xff));
+ num++;
+ }
+ }
+ return num;
+ }
+
+ /**
+ * Reads a single character.
+ *
+ * @return The character read, as an integer in the range 0 to 65535
+ * (0x0000-0xffff), or -1 if the end of the stream has been reached.
+ * @exception IOException If any IO errors occur.
+ */
+ public int read() throws IOException {
+ int val;
+ if ((val = is.read()) < 0) {
+ return -1;
+ }
+ if (bo == 'b') {
+ val = (char) ((val << 8) | (is.read() & 0xff));
+ } else {
+ val = (char) ((is.read() << 8) | (val & 0xff));
+ }
+ return val;
+ }
+
+ /**
+ * Closes the stream.
+ *
+ * @exception IOException If any IO errors occur.
+ */
+ public void close() throws IOException {
+ is.close();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/ReaderUTF8.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.Reader;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * UTF-8 transformed UCS-2 character stream reader.
+ *
+ * This reader converts UTF-8 transformed UCS-2 characters to Java characters.
+ * The UCS-2 subset of UTF-8 transformation is described in RFC-2279 #2
+ * "UTF-8 definition":
+ * 0000 0000-0000 007F 0xxxxxxx
+ * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
+ * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
+ *
+ * This reader will return incorrect last character on broken UTF-8 stream.
+ */
+public class ReaderUTF8 extends Reader {
+
+ private InputStream is;
+
+ /**
+ * Constructor.
+ *
+ * @param is A byte input stream.
+ */
+ public ReaderUTF8(InputStream is) {
+ this.is = is;
+ }
+
+ /**
+ * Reads characters into a portion of an array.
+ *
+ * @param cbuf Destination buffer.
+ * @param off Offset at which to start storing characters.
+ * @param len Maximum number of characters to read.
+ * @exception IOException If any IO errors occur.
+ * @exception UnsupportedEncodingException If UCS-4 character occur in the stream.
+ */
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ int num = 0;
+ int val;
+ while (num < len) {
+ if ((val = is.read()) < 0) {
+ return (num != 0) ? num : -1;
+ }
+ switch (val & 0xf0) {
+ case 0xc0:
+ case 0xd0:
+ cbuf[off++] = (char) (((val & 0x1f) << 6) | (is.read() & 0x3f));
+ break;
+
+ case 0xe0:
+ cbuf[off++] = (char) (((val & 0x0f) << 12)
+ | ((is.read() & 0x3f) << 6) | (is.read() & 0x3f));
+ break;
+
+ case 0xf0: // UCS-4 character
+ throw new UnsupportedEncodingException("UTF-32 (or UCS-4) encoding not supported.");
+
+ default:
+ cbuf[off++] = (char) val;
+ break;
+ }
+ num++;
+ }
+ return num;
+ }
+
+ /**
+ * Reads a single character.
+ *
+ * @return The character read, as an integer in the range 0 to 65535
+ * (0x00-0xffff), or -1 if the end of the stream has been reached.
+ * @exception IOException If any IO errors occur.
+ * @exception UnsupportedEncodingException If UCS-4 character occur in the stream.
+ */
+ public int read() throws IOException {
+ int val;
+ if ((val = is.read()) < 0) {
+ return -1;
+ }
+ switch (val & 0xf0) {
+ case 0xc0:
+ case 0xd0:
+ val = ((val & 0x1f) << 6) | (is.read() & 0x3f);
+ break;
+
+ case 0xe0:
+ val = ((val & 0x0f) << 12)
+ | ((is.read() & 0x3f) << 6) | (is.read() & 0x3f);
+ break;
+
+ case 0xf0: // UCS-4 character
+ throw new UnsupportedEncodingException();
+
+ default:
+ break;
+ }
+ return val;
+ }
+
+ /**
+ * Closes the stream.
+ *
+ * @exception IOException If any IO errors occur.
+ */
+ public void close() throws IOException {
+ is.close();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/SAXParserImpl.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.SAXException;
+import jdk.internal.org.xml.sax.XMLReader;
+import jdk.internal.org.xml.sax.helpers.DefaultHandler;
+import jdk.internal.util.xml.SAXParser;
+
+public class SAXParserImpl extends SAXParser {
+
+ private ParserSAX parser;
+
+ public SAXParserImpl() {
+ super();
+ parser = new ParserSAX();
+ }
+
+ /**
+ * Returns the {@link org.xml.sax.XMLReader} that is encapsulated by the
+ * implementation of this class.
+ *
+ * @return The XMLReader that is encapsulated by the
+ * implementation of this class.
+ *
+ * @throws SAXException If any SAX errors occur during processing.
+ */
+ public XMLReader getXMLReader()
+ throws SAXException {
+ return parser;
+ }
+
+ /**
+ * Indicates whether or not this parser is configured to
+ * understand namespaces.
+ *
+ * @return true if this parser is configured to
+ * understand namespaces; false otherwise.
+ */
+ public boolean isNamespaceAware() {
+ return parser.mIsNSAware;
+ }
+
+ /**
+ * Indicates whether or not this parser is configured to validate
+ * XML documents.
+ *
+ * @return true if this parser is configured to validate XML
+ * documents; false otherwise.
+ */
+ public boolean isValidating() {
+ return false;
+ }
+
+ /**
+ * Parse the content of the given {@link java.io.InputStream}
+ * instance as XML using the specified
+ * {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param src InputStream containing the content to be parsed.
+ * @param handler The SAX DefaultHandler to use.
+ * @exception IOException If any IO errors occur.
+ * @exception IllegalArgumentException If the given InputStream or handler is null.
+ * @exception SAXException If the underlying parser throws a
+ * SAXException while parsing.
+ * @see org.xml.sax.helpers.DefaultHandler
+ */
+ public void parse(InputStream src, DefaultHandler handler)
+ throws SAXException, IOException
+ {
+ parser.parse(src, handler);
+ }
+
+ /**
+ * Parse the content given {@link org.xml.sax.InputSource}
+ * as XML using the specified
+ * {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param is The InputSource containing the content to be parsed.
+ * @param handler The SAX DefaultHandler to use.
+ * @exception IOException If any IO errors occur.
+ * @exception IllegalArgumentException If the InputSource or handler is null.
+ * @exception SAXException If the underlying parser throws a
+ * SAXException while parsing.
+ * @see org.xml.sax.helpers.DefaultHandler
+ */
+ public void parse(InputSource is, DefaultHandler handler)
+ throws SAXException, IOException
+ {
+ parser.parse(is, handler);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/XMLStreamWriterImpl.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,633 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
+import jdk.internal.util.xml.XMLStreamException;
+import jdk.internal.util.xml.XMLStreamWriter;
+
+/**
+ * Implementation of a reduced version of XMLStreamWriter
+ *
+ * @author Joe Wang
+ */
+public class XMLStreamWriterImpl implements XMLStreamWriter {
+ //Document state
+
+ static final int STATE_XML_DECL = 1;
+ static final int STATE_PROLOG = 2;
+ static final int STATE_DTD_DECL = 3;
+ static final int STATE_ELEMENT = 4;
+ //Element state
+ static final int ELEMENT_STARTTAG_OPEN = 10;
+ static final int ELEMENT_STARTTAG_CLOSE = 11;
+ static final int ELEMENT_ENDTAG_OPEN = 12;
+ static final int ELEMENT_ENDTAG_CLOSE = 13;
+ public static final char CLOSE_START_TAG = '>';
+ public static final char OPEN_START_TAG = '<';
+ public static final String OPEN_END_TAG = "</";
+ public static final char CLOSE_END_TAG = '>';
+ public static final String START_CDATA = "<![CDATA[";
+ public static final String END_CDATA = "]]>";
+ public static final String CLOSE_EMPTY_ELEMENT = "/>";
+ public static final String ENCODING_PREFIX = "&#x";
+ public static final char SPACE = ' ';
+ public static final char AMPERSAND = '&';
+ public static final char DOUBLEQUOT = '"';
+ public static final char SEMICOLON = ';';
+ //current state
+ private int _state = 0;
+ private Element _currentEle;
+ private XMLWriter _writer;
+ private String _encoding;
+ /**
+ * This flag can be used to turn escaping off for content. It does
+ * not apply to attribute content.
+ */
+ boolean _escapeCharacters = true;
+ //pretty print by default
+ private boolean _doIndent = true;
+ //The system line separator for writing out line breaks.
+ private char[] _lineSep =
+ System.getProperty("line.separator").toCharArray();
+
+ public XMLStreamWriterImpl(OutputStream os) throws XMLStreamException {
+ this(os, XMLStreamWriter.DEFAULT_ENCODING);
+ }
+
+ public XMLStreamWriterImpl(OutputStream os, String encoding)
+ throws XMLStreamException
+ {
+ Charset cs = null;
+ if (encoding == null) {
+ _encoding = XMLStreamWriter.DEFAULT_ENCODING;
+ } else {
+ try {
+ cs = getCharset(encoding);
+ } catch (UnsupportedEncodingException e) {
+ throw new XMLStreamException(e);
+ }
+
+ this._encoding = encoding;
+ }
+
+ _writer = new XMLWriter(os, encoding, cs);
+ }
+
+ /**
+ * Write the XML Declaration. Defaults the XML version to 1.0, and the
+ * encoding to utf-8.
+ *
+ * @throws XMLStreamException
+ */
+ public void writeStartDocument() throws XMLStreamException {
+ writeStartDocument(_encoding, XMLStreamWriter.DEFAULT_XML_VERSION);
+ }
+
+ /**
+ * Write the XML Declaration. Defaults the encoding to utf-8
+ *
+ * @param version version of the xml document
+ * @throws XMLStreamException
+ */
+ public void writeStartDocument(String version) throws XMLStreamException {
+ writeStartDocument(_encoding, version, null);
+ }
+
+ /**
+ * Write the XML Declaration. Note that the encoding parameter does not set
+ * the actual encoding of the underlying output. That must be set when the
+ * instance of the XMLStreamWriter is created
+ *
+ * @param encoding encoding of the xml declaration
+ * @param version version of the xml document
+ * @throws XMLStreamException If given encoding does not match encoding of the
+ * underlying stream
+ */
+ public void writeStartDocument(String encoding, String version) throws XMLStreamException {
+ writeStartDocument(encoding, version, null);
+ }
+
+ /**
+ * Write the XML Declaration. Note that the encoding parameter does not set
+ * the actual encoding of the underlying output. That must be set when the
+ * instance of the XMLStreamWriter is created
+ *
+ * @param encoding encoding of the xml declaration
+ * @param version version of the xml document
+ * @param standalone indicate if the xml document is standalone
+ * @throws XMLStreamException If given encoding does not match encoding of the
+ * underlying stream
+ */
+ public void writeStartDocument(String encoding, String version, String standalone)
+ throws XMLStreamException
+ {
+ if (_state > 0) {
+ throw new XMLStreamException("XML declaration must be as the first line in the XML document.");
+ }
+ _state = STATE_XML_DECL;
+ String enc = encoding;
+ if (enc == null) {
+ enc = _encoding;
+ } else {
+ //check if the encoding is supported
+ try {
+ getCharset(encoding);
+ } catch (UnsupportedEncodingException e) {
+ throw new XMLStreamException(e);
+ }
+ }
+
+ if (version == null) {
+ version = XMLStreamWriter.DEFAULT_XML_VERSION;
+ }
+
+ _writer.write("<?xml version=\"");
+ _writer.write(version);
+ _writer.write(DOUBLEQUOT);
+
+ if (enc != null) {
+ _writer.write(" encoding=\"");
+ _writer.write(enc);
+ _writer.write(DOUBLEQUOT);
+ }
+
+ if (standalone != null) {
+ _writer.write(" standalone=\"");
+ _writer.write(standalone);
+ _writer.write(DOUBLEQUOT);
+ }
+ _writer.write("?>");
+ writeLineSeparator();
+ }
+
+ /**
+ * Write a DTD section. This string represents the entire doctypedecl production
+ * from the XML 1.0 specification.
+ *
+ * @param dtd the DTD to be written
+ * @throws XMLStreamException
+ */
+ public void writeDTD(String dtd) throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+ _writer.write(dtd);
+ writeLineSeparator();
+ }
+
+ /**
+ * Writes a start tag to the output.
+ * @param localName local name of the tag, may not be null
+ * @throws XMLStreamException
+ */
+ public void writeStartElement(String localName) throws XMLStreamException {
+ if (localName == null || localName.length() == 0) {
+ throw new XMLStreamException("Local Name cannot be null or empty");
+ }
+
+ _state = STATE_ELEMENT;
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ _currentEle = new Element(_currentEle, localName, false);
+ openStartTag();
+
+ _writer.write(localName);
+ }
+
+ /**
+ * Writes an empty element tag to the output
+ * @param localName local name of the tag, may not be null
+ * @throws XMLStreamException
+ */
+ public void writeEmptyElement(String localName) throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ _currentEle = new Element(_currentEle, localName, true);
+
+ openStartTag();
+ _writer.write(localName);
+ }
+
+ /**
+ * Writes an attribute to the output stream without a prefix.
+ * @param localName the local name of the attribute
+ * @param value the value of the attribute
+ * @throws IllegalStateException if the current state does not allow Attribute writing
+ * @throws XMLStreamException
+ */
+ public void writeAttribute(String localName, String value) throws XMLStreamException {
+ if (_currentEle.getState() != ELEMENT_STARTTAG_OPEN) {
+ throw new XMLStreamException(
+ "Attribute not associated with any element");
+ }
+
+ _writer.write(SPACE);
+ _writer.write(localName);
+ _writer.write("=\"");
+ writeXMLContent(
+ value,
+ true, // true = escapeChars
+ true); // true = escapeDoubleQuotes
+ _writer.write(DOUBLEQUOT);
+ }
+
+ public void writeEndDocument() throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ /**
+ * close unclosed elements if any
+ */
+ while (_currentEle != null) {
+
+ if (!_currentEle.isEmpty()) {
+ _writer.write(OPEN_END_TAG);
+ _writer.write(_currentEle.getLocalName());
+ _writer.write(CLOSE_END_TAG);
+ }
+
+ _currentEle = _currentEle.getParent();
+ }
+ }
+
+ public void writeEndElement() throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ if (_currentEle == null) {
+ throw new XMLStreamException("No element was found to write");
+ }
+
+ if (_currentEle.isEmpty()) {
+ return;
+ }
+
+ _writer.write(OPEN_END_TAG);
+ _writer.write(_currentEle.getLocalName());
+ _writer.write(CLOSE_END_TAG);
+ writeLineSeparator();
+
+ _currentEle = _currentEle.getParent();
+ }
+
+ public void writeCData(String cdata) throws XMLStreamException {
+ if (cdata == null) {
+ throw new XMLStreamException("cdata cannot be null");
+ }
+
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ _writer.write(START_CDATA);
+ _writer.write(cdata);
+ _writer.write(END_CDATA);
+ }
+
+ public void writeCharacters(String data) throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ writeXMLContent(data);
+ }
+
+ public void writeCharacters(char[] data, int start, int len)
+ throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ writeXMLContent(data, start, len, _escapeCharacters);
+ }
+
+ /**
+ * Close this XMLStreamWriter by closing underlying writer.
+ */
+ public void close() throws XMLStreamException {
+ if (_writer != null) {
+ _writer.close();
+ }
+ _writer = null;
+ _currentEle = null;
+ _state = 0;
+ }
+
+ /**
+ * Flush this XMLStreamWriter by flushing underlying writer.
+ */
+ public void flush() throws XMLStreamException {
+ if (_writer != null) {
+ _writer.flush();
+ }
+ }
+
+ /**
+ * Set the flag to indicate if the writer should add line separator
+ * @param doIndent
+ */
+ public void setDoIndent(boolean doIndent) {
+ _doIndent = doIndent;
+ }
+
+ /**
+ * Writes XML content to underlying writer. Escapes characters unless
+ * escaping character feature is turned off.
+ */
+ private void writeXMLContent(char[] content, int start, int length, boolean escapeChars)
+ throws XMLStreamException
+ {
+ if (!escapeChars) {
+ _writer.write(content, start, length);
+ return;
+ }
+
+ // Index of the next char to be written
+ int startWritePos = start;
+
+ final int end = start + length;
+
+ for (int index = start; index < end; index++) {
+ char ch = content[index];
+
+ if (!_writer.canEncode(ch)) {
+ _writer.write(content, startWritePos, index - startWritePos);
+
+ // Escape this char as underlying encoder cannot handle it
+ _writer.write(ENCODING_PREFIX);
+ _writer.write(Integer.toHexString(ch));
+ _writer.write(SEMICOLON);
+ startWritePos = index + 1;
+ continue;
+ }
+
+ switch (ch) {
+ case OPEN_START_TAG:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write("<");
+ startWritePos = index + 1;
+
+ break;
+
+ case AMPERSAND:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write("&");
+ startWritePos = index + 1;
+
+ break;
+
+ case CLOSE_START_TAG:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write(">");
+ startWritePos = index + 1;
+
+ break;
+ }
+ }
+
+ // Write any pending data
+ _writer.write(content, startWritePos, end - startWritePos);
+ }
+
+ private void writeXMLContent(String content) throws XMLStreamException {
+ if ((content != null) && (content.length() > 0)) {
+ writeXMLContent(content,
+ _escapeCharacters, // boolean = escapeChars
+ false); // false = escapeDoubleQuotes
+ }
+ }
+
+ /**
+ * Writes XML content to underlying writer. Escapes characters unless
+ * escaping character feature is turned off.
+ */
+ private void writeXMLContent(
+ String content,
+ boolean escapeChars,
+ boolean escapeDoubleQuotes)
+ throws XMLStreamException
+ {
+
+ if (!escapeChars) {
+ _writer.write(content);
+
+ return;
+ }
+
+ // Index of the next char to be written
+ int startWritePos = 0;
+
+ final int end = content.length();
+
+ for (int index = 0; index < end; index++) {
+ char ch = content.charAt(index);
+
+ if (!_writer.canEncode(ch)) {
+ _writer.write(content, startWritePos, index - startWritePos);
+
+ // Escape this char as underlying encoder cannot handle it
+ _writer.write(ENCODING_PREFIX);
+ _writer.write(Integer.toHexString(ch));
+ _writer.write(SEMICOLON);
+ startWritePos = index + 1;
+ continue;
+ }
+
+ switch (ch) {
+ case OPEN_START_TAG:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write("<");
+ startWritePos = index + 1;
+
+ break;
+
+ case AMPERSAND:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write("&");
+ startWritePos = index + 1;
+
+ break;
+
+ case CLOSE_START_TAG:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write(">");
+ startWritePos = index + 1;
+
+ break;
+
+ case DOUBLEQUOT:
+ _writer.write(content, startWritePos, index - startWritePos);
+ if (escapeDoubleQuotes) {
+ _writer.write(""");
+ } else {
+ _writer.write(DOUBLEQUOT);
+ }
+ startWritePos = index + 1;
+
+ break;
+ }
+ }
+
+ // Write any pending data
+ _writer.write(content, startWritePos, end - startWritePos);
+ }
+
+ /**
+ * marks open of start tag and writes the same into the writer.
+ */
+ private void openStartTag() throws XMLStreamException {
+ _currentEle.setState(ELEMENT_STARTTAG_OPEN);
+ _writer.write(OPEN_START_TAG);
+ }
+
+ /**
+ * marks close of start tag and writes the same into the writer.
+ */
+ private void closeStartTag() throws XMLStreamException {
+ if (_currentEle.isEmpty()) {
+ _writer.write(CLOSE_EMPTY_ELEMENT);
+ } else {
+ _writer.write(CLOSE_START_TAG);
+
+ }
+
+ if (_currentEle.getParent() == null) {
+ writeLineSeparator();
+ }
+
+ _currentEle.setState(ELEMENT_STARTTAG_CLOSE);
+
+ }
+
+ /**
+ * Write a line separator
+ * @throws XMLStreamException
+ */
+ private void writeLineSeparator() throws XMLStreamException {
+ if (_doIndent) {
+ _writer.write(_lineSep, 0, _lineSep.length);
+ }
+ }
+
+ /**
+ * Returns a charset object for the specified encoding
+ * @param encoding
+ * @return a charset object
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ private Charset getCharset(String encoding) throws UnsupportedEncodingException {
+ if (encoding.equalsIgnoreCase("UTF-32")) {
+ throw new UnsupportedEncodingException("The basic XMLWriter does "
+ + "not support " + encoding);
+ }
+
+ Charset cs;
+ try {
+ cs = Charset.forName(encoding);
+ } catch (IllegalCharsetNameException | UnsupportedCharsetException ex) {
+ throw new UnsupportedEncodingException(encoding);
+ }
+ return cs;
+ }
+
+ /*
+ * Start of Internal classes.
+ *
+ */
+ protected class Element {
+
+ /**
+ * the parent element
+ */
+ protected Element _parent;
+ /**
+ * The size of the stack.
+ */
+ protected short _Depth;
+ /**
+ * indicate if an element is an empty one
+ */
+ boolean _isEmptyElement = false;
+ String _localpart;
+ int _state;
+
+ /**
+ * Default constructor.
+ */
+ public Element() {
+ }
+
+ /**
+ * @param parent the parent of the element
+ * @param localpart name of the element
+ * @param isEmpty indicate if the element is an empty one
+ */
+ public Element(Element parent, String localpart, boolean isEmpty) {
+ _parent = parent;
+ _localpart = localpart;
+ _isEmptyElement = isEmpty;
+ }
+
+ public Element getParent() {
+ return _parent;
+ }
+
+ public String getLocalName() {
+ return _localpart;
+ }
+
+ /**
+ * get the state of the element
+ */
+ public int getState() {
+ return _state;
+ }
+
+ /**
+ * Set the state of the element
+ *
+ * @param state the state of the element
+ */
+ public void setState(int state) {
+ _state = state;
+ }
+
+ public boolean isEmpty() {
+ return _isEmptyElement;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/XMLWriter.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import jdk.internal.util.xml.XMLStreamException;
+
+/**
+ *
+ * @author huizwang
+ */
+public class XMLWriter {
+
+ private Writer _writer;
+ /**
+ * In some cases, this charset encoder is used to determine if a char is
+ * encodable by underlying writer. For example, an 8-bit char from the
+ * extended ASCII set is not encodable by 7-bit ASCII encoder. Unencodable
+ * chars are escaped using XML numeric entities.
+ */
+ private CharsetEncoder _encoder = null;
+
+ public XMLWriter(OutputStream os, String encoding, Charset cs) throws XMLStreamException {
+ _encoder = cs.newEncoder();
+ try {
+ _writer = getWriter(os, encoding, cs);
+ } catch (UnsupportedEncodingException ex) {
+ throw new XMLStreamException(ex);
+ }
+
+ }
+
+ public boolean canEncode(char ch) {
+ if (_encoder == null) {
+ return false;
+ }
+ return (_encoder.canEncode(ch));
+ }
+
+ public void write(String s)
+ throws XMLStreamException {
+ try {
+ _writer.write(s.toCharArray());
+// _writer.write(s.getBytes(Charset.forName(_encoding)));
+ } catch (IOException e) {
+ throw new XMLStreamException("I/O error", e);
+ }
+ }
+
+ public void write(String str, int off, int len)
+ throws XMLStreamException {
+ try {
+ _writer.write(str, off, len);
+ } catch (IOException e) {
+ throw new XMLStreamException("I/O error", e);
+ }
+
+ }
+
+ public void write(char[] cbuf, int off, int len)
+ throws XMLStreamException {
+ try {
+ _writer.write(cbuf, off, len);
+ } catch (IOException e) {
+ throw new XMLStreamException("I/O error", e);
+ }
+
+ }
+
+ void write(int b)
+ throws XMLStreamException {
+ try {
+ _writer.write(b);
+ } catch (IOException e) {
+ throw new XMLStreamException("I/O error", e);
+ }
+ }
+
+ void flush() throws XMLStreamException {
+ try {
+ _writer.flush();
+ } catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ }
+
+ void close() throws XMLStreamException {
+ try {
+ _writer.close();
+ } catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ }
+
+ private void nl() throws XMLStreamException {
+ String lineEnd = System.getProperty("line.separator");
+ try {
+ _writer.write(lineEnd);
+ } catch (IOException e) {
+ throw new XMLStreamException("I/O error", e);
+ }
+ }
+
+ /**
+ * Returns a writer for the specified encoding based on an output stream.
+ *
+ * @param output The output stream
+ * @param encoding The encoding
+ * @return A suitable writer
+ * @throws UnsupportedEncodingException There is no convertor to support
+ * this encoding
+ */
+ private Writer getWriter(OutputStream output, String encoding, Charset cs)
+ throws XMLStreamException, UnsupportedEncodingException
+ {
+ if (cs != null) {
+ return (new OutputStreamWriter(new BufferedOutputStream(output), cs));
+ }
+
+ return new OutputStreamWriter(new BufferedOutputStream(output), encoding);
+ }
+}
--- a/jdk/src/share/classes/sun/net/www/http/KeepAliveStream.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/sun/net/www/http/KeepAliveStream.java Fri Dec 28 18:36:41 2012 -0800
@@ -83,11 +83,8 @@
if (expected > count) {
long nskip = expected - count;
if (nskip <= available()) {
- long n = 0;
- while (n < nskip) {
- nskip = nskip - n;
- n = skip(nskip);
- }
+ do {} while ((nskip = (expected - count)) > 0L
+ && skip(Math.min(nskip, available())) > 0L);
} else if (expected <= KeepAliveStreamCleaner.MAX_DATA_REMAINING && !hurried) {
//put this KeepAliveStream on the queue so that the data remaining
//on the socket can be cleanup asyncronously.
--- a/jdk/src/share/classes/sun/security/krb5/EncryptionKey.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/sun/security/krb5/EncryptionKey.java Fri Dec 28 18:36:41 2012 -0800
@@ -555,6 +555,12 @@
int ktype;
boolean etypeFound = false;
+
+ // When no matched kvno is found, returns tke key of the same
+ // etype with the highest kvno
+ int kvno_found = 0;
+ EncryptionKey key_found = null;
+
for (int i = 0; i < keys.length; i++) {
ktype = keys[i].getEType();
if (EType.isSupported(ktype)) {
@@ -563,6 +569,10 @@
etypeFound = true;
if (versionMatches(kvno, kv)) {
return keys[i];
+ } else if (kv > kvno_found) {
+ // kv is not null
+ key_found = keys[i];
+ kvno_found = kv;
}
}
}
@@ -580,12 +590,17 @@
etypeFound = true;
if (versionMatches(kvno, kv)) {
return new EncryptionKey(etype, keys[i].getBytes());
+ } else if (kv > kvno_found) {
+ key_found = new EncryptionKey(etype, keys[i].getBytes());
+ kvno_found = kv;
}
}
}
}
if (etypeFound) {
- throw new KrbException(Krb5.KRB_AP_ERR_BADKEYVER);
+ return key_found;
+ // For compatibility, will not fail here.
+ //throw new KrbException(Krb5.KRB_AP_ERR_BADKEYVER);
}
return null;
}
--- a/jdk/src/share/classes/sun/security/provider/ConfigSpiFile.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/sun/security/provider/ConfigSpiFile.java Fri Dec 28 18:36:41 2012 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2012, 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
@@ -25,81 +25,668 @@
package sun.security.provider;
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.Security;
import java.security.URIParameter;
-
+import java.text.MessageFormat;
+import java.util.*;
+import javax.security.auth.AuthPermission;
+import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.ConfigurationSpi;
-import javax.security.auth.login.AppConfigurationEntry;
-
-import com.sun.security.auth.login.ConfigFile;
+import sun.security.util.Debug;
+import sun.security.util.PropertyExpander;
+import sun.security.util.ResourcesMgr;
/**
- * This class wraps the ConfigFile subclass implementation of Configuration
- * inside a ConfigurationSpi implementation that is available from the
- * SUN provider via the Configuration.getInstance calls.
+ * This class represents a default implementation for
+ * {@code javax.security.auth.login.Configuration}.
+ *
+ * <p> This object stores the runtime login configuration representation,
+ * and is the amalgamation of multiple static login
+ * configurations that resides in files.
+ * The algorithm for locating the login configuration file(s) and reading their
+ * information into this {@code Configuration} object is:
+ *
+ * <ol>
+ * <li>
+ * Loop through the security properties,
+ * <i>login.config.url.1</i>, <i>login.config.url.2</i>, ...,
+ * <i>login.config.url.X</i>.
+ * Each property value specifies a <code>URL</code> pointing to a
+ * login configuration file to be loaded. Read in and load
+ * each configuration.
*
+ * <li>
+ * The {@code java.lang.System} property
+ * <i>java.security.auth.login.config</i>
+ * may also be set to a {@code URL} pointing to another
+ * login configuration file
+ * (which is the case when a user uses the -D switch at runtime).
+ * If this property is defined, and its use is allowed by the
+ * security property file (the Security property,
+ * <i>policy.allowSystemProperty</i> is set to <i>true</i>),
+ * also load that login configuration.
+ *
+ * <li>
+ * If the <i>java.security.auth.login.config</i> property is defined using
+ * "==" (rather than "="), then ignore all other specified
+ * login configurations and only load this configuration.
+ *
+ * <li>
+ * If no system or security properties were set, try to read from the file,
+ * ${user.home}/.java.login.config, where ${user.home} is the value
+ * represented by the "user.home" System property.
+ * </ol>
+ *
+ * <p> The configuration syntax supported by this implementation
+ * is exactly that syntax specified in the
+ * {@code javax.security.auth.login.Configuration} class.
+ *
+ * @see javax.security.auth.login.LoginContext
+ * @see java.security.Security security properties
*/
public final class ConfigSpiFile extends ConfigurationSpi {
- private ConfigFile cf;
+ private URL url;
+ private boolean expandProp = true;
+ private Map<String, List<AppConfigurationEntry>> configuration;
+ private int linenum;
+ private StreamTokenizer st;
+ private int lookahead;
+
+ private static Debug debugConfig = Debug.getInstance("configfile");
+ private static Debug debugParser = Debug.getInstance("configparser");
+
+ /**
+ * Create a new {@code Configuration} object.
+ *
+ * @throws SecurityException if the {@code Configuration} can not be
+ * initialized
+ */
+ public ConfigSpiFile() {
+ try {
+ init();
+ } catch (IOException ioe) {
+ throw new SecurityException(ioe);
+ }
+ }
+
+ /**
+ * Create a new {@code Configuration} object from the specified {@code URI}.
+ *
+ * @param uri the {@code URI}
+ * @throws SecurityException if the {@code Configuration} can not be
+ * initialized
+ * @throws NullPointerException if {@code uri} is null
+ */
+ public ConfigSpiFile(URI uri) {
+ // only load config from the specified URI
+ try {
+ url = uri.toURL();
+ init();
+ } catch (IOException ioe) {
+ throw new SecurityException(ioe);
+ }
+ }
public ConfigSpiFile(final Configuration.Parameters params)
- throws java.io.IOException {
+ throws IOException {
// call in a doPrivileged
//
// we have already passed the Configuration.getInstance
// security check. also this class is not freely accessible
// (it is in the "sun" package).
- //
- // we can not put doPrivileged calls into
- // ConfigFile because it is a public com.sun class
try {
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+ public Void run() throws IOException {
+ if (params == null) {
+ init();
+ } else {
+ if (!(params instanceof URIParameter)) {
+ throw new IllegalArgumentException
+ ("Unrecognized parameter: " + params);
+ }
+ URIParameter uriParam = (URIParameter)params;
+ url = uriParam.getURI().toURL();
+ init();
+ }
+ return null;
+ }
+ });
+ } catch (PrivilegedActionException pae) {
+ throw (IOException)pae.getException();
+ }
+
+ // if init() throws some other RuntimeException,
+ // let it percolate up naturally.
+ }
+
+ /**
+ * Read and initialize the entire login Configuration from the configured
+ * URL.
+ *
+ * @throws IOException if the Configuration can not be initialized
+ * @throws SecurityException if the caller does not have permission
+ * to initialize the Configuration
+ */
+ private void init() throws IOException {
+
+ boolean initialized = false;
+
+ // For policy.expandProperties, check if either a security or system
+ // property is set to false (old code erroneously checked the system
+ // prop so we must check both to preserve compatibility).
+ String expand = Security.getProperty("policy.expandProperties");
+ if (expand == null) {
+ expand = System.getProperty("policy.expandProperties");
+ }
+ if ("false".equals(expand)) {
+ expandProp = false;
+ }
+
+ // new configuration
+ Map<String, List<AppConfigurationEntry>> newConfig = new HashMap<>();
+
+ if (url != null) {
+ /**
+ * If the caller specified a URI via Configuration.getInstance,
+ * we only read from that URI
+ */
+ if (debugConfig != null) {
+ debugConfig.println("reading " + url);
+ }
+ init(url, newConfig);
+ configuration = newConfig;
+ return;
+ }
+
+ /**
+ * Caller did not specify URI via Configuration.getInstance.
+ * Read from URLs listed in the java.security properties file.
+ */
+ String allowSys = Security.getProperty("policy.allowSystemProperty");
+
+ if ("true".equalsIgnoreCase(allowSys)) {
+ String extra_config = System.getProperty
+ ("java.security.auth.login.config");
+ if (extra_config != null) {
+ boolean overrideAll = false;
+ if (extra_config.startsWith("=")) {
+ overrideAll = true;
+ extra_config = extra_config.substring(1);
+ }
+ try {
+ extra_config = PropertyExpander.expand(extra_config);
+ } catch (PropertyExpander.ExpandException peee) {
+ MessageFormat form = new MessageFormat
+ (ResourcesMgr.getString
+ ("Unable.to.properly.expand.config",
+ "sun.security.util.AuthResources"));
+ Object[] source = {extra_config};
+ throw new IOException(form.format(source));
+ }
+
+ URL configURL = null;
+ try {
+ configURL = new URL(extra_config);
+ } catch (MalformedURLException mue) {
+ File configFile = new File(extra_config);
+ if (configFile.exists()) {
+ configURL = configFile.toURI().toURL();
+ } else {
+ MessageFormat form = new MessageFormat
+ (ResourcesMgr.getString
+ ("extra.config.No.such.file.or.directory.",
+ "sun.security.util.AuthResources"));
+ Object[] source = {extra_config};
+ throw new IOException(form.format(source));
+ }
+ }
+
+ if (debugConfig != null) {
+ debugConfig.println("reading "+configURL);
+ }
+ init(configURL, newConfig);
+ initialized = true;
+ if (overrideAll) {
+ if (debugConfig != null) {
+ debugConfig.println("overriding other policies!");
+ }
+ configuration = newConfig;
+ return;
+ }
+ }
+ }
+
+ int n = 1;
+ String config_url;
+ while ((config_url = Security.getProperty
+ ("login.config.url."+n)) != null) {
+ try {
+ config_url = PropertyExpander.expand
+ (config_url).replace(File.separatorChar, '/');
+ if (debugConfig != null) {
+ debugConfig.println("\tReading config: " + config_url);
+ }
+ init(new URL(config_url), newConfig);
+ initialized = true;
+ } catch (PropertyExpander.ExpandException peee) {
+ MessageFormat form = new MessageFormat
+ (ResourcesMgr.getString
+ ("Unable.to.properly.expand.config",
+ "sun.security.util.AuthResources"));
+ Object[] source = {config_url};
+ throw new IOException(form.format(source));
+ }
+ n++;
+ }
+
+ if (initialized == false && n == 1 && config_url == null) {
+
+ // get the config from the user's home directory
+ if (debugConfig != null) {
+ debugConfig.println("\tReading Policy " +
+ "from ~/.java.login.config");
+ }
+ config_url = System.getProperty("user.home");
+ String userConfigFile = config_url +
+ File.separatorChar + ".java.login.config";
+
+ // No longer throws an exception when there's no config file
+ // at all. Returns an empty Configuration instead.
+ if (new File(userConfigFile).exists()) {
+ init(new File(userConfigFile).toURI().toURL(),
+ newConfig);
+ }
+ }
+
+ configuration = newConfig;
+ }
+
+ private void init(URL config,
+ Map<String, List<AppConfigurationEntry>> newConfig)
+ throws IOException {
+
+ try (InputStreamReader isr
+ = new InputStreamReader(getInputStream(config), "UTF-8")) {
+ readConfig(isr, newConfig);
+ } catch (FileNotFoundException fnfe) {
+ if (debugConfig != null) {
+ debugConfig.println(fnfe.toString());
+ }
+ throw new IOException(ResourcesMgr.getString
+ ("Configuration.Error.No.such.file.or.directory",
+ "sun.security.util.AuthResources"));
+ }
+ }
+
+ /**
+ * Retrieve an entry from the Configuration using an application name
+ * as an index.
+ *
+ * @param applicationName the name used to index the Configuration.
+ * @return an array of AppConfigurationEntries which correspond to
+ * the stacked configuration of LoginModules for this
+ * application, or null if this application has no configured
+ * LoginModules.
+ */
+ @Override
+ public AppConfigurationEntry[] engineGetAppConfigurationEntry
+ (String applicationName) {
+
+ List<AppConfigurationEntry> list = null;
+ synchronized (configuration) {
+ list = configuration.get(applicationName);
+ }
+
+ if (list == null || list.size() == 0)
+ return null;
+
+ AppConfigurationEntry[] entries =
+ new AppConfigurationEntry[list.size()];
+ Iterator<AppConfigurationEntry> iterator = list.iterator();
+ for (int i = 0; iterator.hasNext(); i++) {
+ AppConfigurationEntry e = iterator.next();
+ entries[i] = new AppConfigurationEntry(e.getLoginModuleName(),
+ e.getControlFlag(),
+ e.getOptions());
+ }
+ return entries;
+ }
+
+ /**
+ * Refresh and reload the Configuration by re-reading all of the
+ * login configurations.
+ *
+ * @throws SecurityException if the caller does not have permission
+ * to refresh the Configuration.
+ */
+ @Override
+ public synchronized void engineRefresh() {
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new AuthPermission("refreshLoginConfiguration"));
+
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
- if (params == null) {
- cf = new ConfigFile();
- } else {
- if (!(params instanceof URIParameter)) {
- throw new IllegalArgumentException
- ("Unrecognized parameter: " + params);
- }
- URIParameter uriParam = (URIParameter)params;
-
- cf = new ConfigFile(uriParam.getURI());
+ try {
+ init();
+ } catch (IOException ioe) {
+ throw new SecurityException(ioe.getLocalizedMessage(), ioe);
}
return null;
}
- });
- } catch (SecurityException se) {
+ });
+ }
+
+ private void readConfig(Reader reader,
+ Map<String, List<AppConfigurationEntry>> newConfig)
+ throws IOException {
+
+ linenum = 1;
+
+ if (!(reader instanceof BufferedReader))
+ reader = new BufferedReader(reader);
+
+ st = new StreamTokenizer(reader);
+ st.quoteChar('"');
+ st.wordChars('$', '$');
+ st.wordChars('_', '_');
+ st.wordChars('-', '-');
+ st.lowerCaseMode(false);
+ st.slashSlashComments(true);
+ st.slashStarComments(true);
+ st.eolIsSignificant(true);
+
+ lookahead = nextToken();
+ while (lookahead != StreamTokenizer.TT_EOF) {
+ parseLoginEntry(newConfig);
+ }
+ }
+
+ private void parseLoginEntry(
+ Map<String, List<AppConfigurationEntry>> newConfig)
+ throws IOException {
+
+ List<AppConfigurationEntry> configEntries = new LinkedList<>();
+
+ // application name
+ String appName = st.sval;
+ lookahead = nextToken();
+
+ if (debugParser != null) {
+ debugParser.println("\tReading next config entry: " + appName);
+ }
+
+ match("{");
- // if ConfigFile threw a standalone SecurityException
- // (no cause), re-throw it.
- //
- // ConfigFile chains checked IOExceptions to SecurityException.
+ // get the modules
+ while (peek("}") == false) {
+ // get the module class name
+ String moduleClass = match("module class name");
- Throwable cause = se.getCause();
- if (cause != null && cause instanceof java.io.IOException) {
- throw (java.io.IOException)cause;
+ // controlFlag (required, optional, etc)
+ AppConfigurationEntry.LoginModuleControlFlag controlFlag;
+ String sflag = match("controlFlag").toUpperCase();
+ switch (sflag) {
+ case "REQUIRED":
+ controlFlag =
+ AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
+ break;
+ case "REQUISITE":
+ controlFlag =
+ AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
+ break;
+ case "SUFFICIENT":
+ controlFlag =
+ AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
+ break;
+ case "OPTIONAL":
+ controlFlag =
+ AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
+ break;
+ default:
+ MessageFormat form = new MessageFormat(
+ ResourcesMgr.getString
+ ("Configuration.Error.Invalid.control.flag.flag",
+ "sun.security.util.AuthResources"));
+ Object[] source = {sflag};
+ throw new IOException(form.format(source));
+ }
+
+ // get the args
+ Map<String, String> options = new HashMap<>();
+ while (peek(";") == false) {
+ String key = match("option key");
+ match("=");
+ try {
+ options.put(key, expand(match("option value")));
+ } catch (PropertyExpander.ExpandException peee) {
+ throw new IOException(peee.getLocalizedMessage());
+ }
}
- // unrecognized cause
- throw se;
+ lookahead = nextToken();
+
+ // create the new element
+ if (debugParser != null) {
+ debugParser.println("\t\t" + moduleClass + ", " + sflag);
+ for (String key : options.keySet()) {
+ debugParser.println("\t\t\t" + key +
+ "=" + options.get(key));
+ }
+ }
+ configEntries.add(new AppConfigurationEntry(moduleClass,
+ controlFlag, options));
}
- // if ConfigFile throws some other RuntimeException,
- // let it percolate up naturally.
+ match("}");
+ match(";");
+
+ // add this configuration entry
+ if (newConfig.containsKey(appName)) {
+ MessageFormat form = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.Can.not.specify.multiple.entries.for.appName",
+ "sun.security.util.AuthResources"));
+ Object[] source = {appName};
+ throw new IOException(form.format(source));
+ }
+ newConfig.put(appName, configEntries);
}
- protected AppConfigurationEntry[] engineGetAppConfigurationEntry
- (String name) {
- return cf.getAppConfigurationEntry(name);
+ private String match(String expect) throws IOException {
+
+ String value = null;
+
+ switch(lookahead) {
+ case StreamTokenizer.TT_EOF:
+
+ MessageFormat form1 = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.expected.expect.read.end.of.file.",
+ "sun.security.util.AuthResources"));
+ Object[] source1 = {expect};
+ throw new IOException(form1.format(source1));
+
+ case '"':
+ case StreamTokenizer.TT_WORD:
+
+ if (expect.equalsIgnoreCase("module class name") ||
+ expect.equalsIgnoreCase("controlFlag") ||
+ expect.equalsIgnoreCase("option key") ||
+ expect.equalsIgnoreCase("option value")) {
+ value = st.sval;
+ lookahead = nextToken();
+ } else {
+ MessageFormat form = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.Line.line.expected.expect.found.value.",
+ "sun.security.util.AuthResources"));
+ Object[] source = {new Integer(linenum), expect, st.sval};
+ throw new IOException(form.format(source));
+ }
+ break;
+
+ case '{':
+
+ if (expect.equalsIgnoreCase("{")) {
+ lookahead = nextToken();
+ } else {
+ MessageFormat form = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.Line.line.expected.expect.",
+ "sun.security.util.AuthResources"));
+ Object[] source = {new Integer(linenum), expect, st.sval};
+ throw new IOException(form.format(source));
+ }
+ break;
+
+ case ';':
+
+ if (expect.equalsIgnoreCase(";")) {
+ lookahead = nextToken();
+ } else {
+ MessageFormat form = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.Line.line.expected.expect.",
+ "sun.security.util.AuthResources"));
+ Object[] source = {new Integer(linenum), expect, st.sval};
+ throw new IOException(form.format(source));
+ }
+ break;
+
+ case '}':
+
+ if (expect.equalsIgnoreCase("}")) {
+ lookahead = nextToken();
+ } else {
+ MessageFormat form = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.Line.line.expected.expect.",
+ "sun.security.util.AuthResources"));
+ Object[] source = {new Integer(linenum), expect, st.sval};
+ throw new IOException(form.format(source));
+ }
+ break;
+
+ case '=':
+
+ if (expect.equalsIgnoreCase("=")) {
+ lookahead = nextToken();
+ } else {
+ MessageFormat form = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.Line.line.expected.expect.",
+ "sun.security.util.AuthResources"));
+ Object[] source = {new Integer(linenum), expect, st.sval};
+ throw new IOException(form.format(source));
+ }
+ break;
+
+ default:
+ MessageFormat form = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.Line.line.expected.expect.found.value.",
+ "sun.security.util.AuthResources"));
+ Object[] source = {new Integer(linenum), expect, st.sval};
+ throw new IOException(form.format(source));
+ }
+ return value;
}
- protected void engineRefresh() {
- cf.refresh();
+ private boolean peek(String expect) {
+ boolean found = false;
+
+ switch (lookahead) {
+ case ',':
+ if (expect.equalsIgnoreCase(","))
+ found = true;
+ break;
+ case ';':
+ if (expect.equalsIgnoreCase(";"))
+ found = true;
+ break;
+ case '{':
+ if (expect.equalsIgnoreCase("{"))
+ found = true;
+ break;
+ case '}':
+ if (expect.equalsIgnoreCase("}"))
+ found = true;
+ break;
+ default:
+ }
+ return found;
+ }
+
+ private int nextToken() throws IOException {
+ int tok;
+ while ((tok = st.nextToken()) == StreamTokenizer.TT_EOL) {
+ linenum++;
+ }
+ return tok;
+ }
+
+ private InputStream getInputStream(URL url) throws IOException {
+ if ("file".equalsIgnoreCase(url.getProtocol())) {
+ // Compatibility notes:
+ //
+ // Code changed from
+ // String path = url.getFile().replace('/', File.separatorChar);
+ // return new FileInputStream(path);
+ //
+ // The original implementation would search for "/tmp/a%20b"
+ // when url is "file:///tmp/a%20b". This is incorrect. The
+ // current codes fix this bug and searches for "/tmp/a b".
+ // For compatibility reasons, when the file "/tmp/a b" does
+ // not exist, the file named "/tmp/a%20b" will be tried.
+ //
+ // This also means that if both file exists, the behavior of
+ // this method is changed, and the current codes choose the
+ // correct one.
+ try {
+ return url.openStream();
+ } catch (Exception e) {
+ String file = url.getPath();
+ if (url.getHost().length() > 0) { // For Windows UNC
+ file = "//" + url.getHost() + file;
+ }
+ if (debugConfig != null) {
+ debugConfig.println("cannot read " + url +
+ ", try " + file);
+ }
+ return new FileInputStream(file);
+ }
+ } else {
+ return url.openStream();
+ }
+ }
+
+ private String expand(String value)
+ throws PropertyExpander.ExpandException, IOException {
+
+ if (value.isEmpty()) {
+ return value;
+ }
+
+ if (expandProp) {
+
+ String s = PropertyExpander.expand(value);
+
+ if (s == null || s.length() == 0) {
+ MessageFormat form = new MessageFormat(ResourcesMgr.getString
+ ("Configuration.Error.Line.line.system.property.value.expanded.to.empty.value",
+ "sun.security.util.AuthResources"));
+ Object[] source = {new Integer(linenum), value};
+ throw new IOException(form.format(source));
+ }
+ return s;
+ } else {
+ return value;
+ }
}
}
--- a/jdk/src/share/classes/sun/text/bidi/BidiBase.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/sun/text/bidi/BidiBase.java Fri Dec 28 18:36:41 2012 -0800
@@ -3251,10 +3251,9 @@
{
verifyValidParaOrLine();
BidiLine.getRuns(this);
- if (runCount == 1) {
+ if (run < 0 || run >= runCount) {
return getParaLevel();
}
- verifyIndex(run, 0, runCount);
getLogicalToVisualRunsMap();
return runs[logicalToVisualRunsMap[run]].level;
}
--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNamesBundle.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNamesBundle.java Fri Dec 28 18:36:41 2012 -0800
@@ -104,7 +104,7 @@
if (contents == null) {
return null;
}
- int clen = Math.min(n, contents.length);
+ int clen = Math.min(n - 1, contents.length);
String[] tmpobj = new String[clen+1];
tmpobj[0] = key;
System.arraycopy(contents, 0, tmpobj, 1, clen);
--- a/jdk/src/share/lib/security/java.security-linux Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/lib/security/java.security-linux Fri Dec 28 18:36:41 2012 -0800
@@ -371,7 +371,7 @@
# jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048
#
#
-jdk.certpath.disabledAlgorithms=MD2
+jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024
# Algorithm restrictions for Secure Socket Layer/Transport Layer Security
# (SSL/TLS) processing
--- a/jdk/src/share/lib/security/java.security-macosx Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/lib/security/java.security-macosx Fri Dec 28 18:36:41 2012 -0800
@@ -372,7 +372,7 @@
# jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048
#
#
-jdk.certpath.disabledAlgorithms=MD2
+jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024
# Algorithm restrictions for Secure Socket Layer/Transport Layer Security
# (SSL/TLS) processing
--- a/jdk/src/share/lib/security/java.security-solaris Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/lib/security/java.security-solaris Fri Dec 28 18:36:41 2012 -0800
@@ -373,7 +373,7 @@
# jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048
#
#
-jdk.certpath.disabledAlgorithms=MD2
+jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024
# Algorithm restrictions for Secure Socket Layer/Transport Layer Security
# (SSL/TLS) processing
--- a/jdk/src/share/lib/security/java.security-windows Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/src/share/lib/security/java.security-windows Fri Dec 28 18:36:41 2012 -0800
@@ -372,7 +372,7 @@
# jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048
#
#
-jdk.certpath.disabledAlgorithms=MD2
+jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024
# Algorithm restrictions for Secure Socket Layer/Transport Layer Security
# (SSL/TLS) processing
--- a/jdk/test/ProblemList.txt Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/ProblemList.txt Fri Dec 28 18:36:41 2012 -0800
@@ -141,9 +141,6 @@
# jdk_management
-# 7158614
-sun/management/jmxremote/startstop/JMXStartStopTest.sh linux-all
-
############################################################################
# jdk_jmx
@@ -151,9 +148,6 @@
# 6959636
javax/management/loading/LibraryLoader/LibraryLoaderTest.java windows-all
-# 7120365
-javax/management/remote/mandatory/notif/DiffHBTest.java generic-all
-
############################################################################
# jdk_math
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/CCAdminReconnectTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/*
+ * @test
+ * @bug 7009998
+ * @summary Tests the correct processing of concurrent ClientComunicatorAdmin reconnect requests.
+ * @author Jaroslav Bachorik
+ * @run clean CCAdminReconnectTest
+ * @run build CCAdminReconnectTest
+ * @run main CCAdminReconnectTest
+ */
+
+import com.sun.jmx.remote.internal.ClientCommunicatorAdmin;
+import java.io.*;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class CCAdminReconnectTest {
+ final private static int THREAD_COUNT = 3;
+
+ public static void main(String ... args) throws Exception {
+ final ExecutorService e = Executors.newFixedThreadPool(THREAD_COUNT);
+ final AtomicBoolean beingReconnected = new AtomicBoolean();
+ final Collection<Exception> thrownExceptions = new LinkedList<>();
+
+ System.out.println(": Testing concurrent restart of ClientCommunicatorAdmin");
+
+ final ClientCommunicatorAdmin cca = new ClientCommunicatorAdmin(50) {
+
+ @Override
+ protected void checkConnection() throws IOException {
+ // empty
+ }
+
+ @Override
+ protected void doStart() throws IOException {
+ if (!beingReconnected.compareAndSet(false, true)) {
+ IOException e = new IOException("Detected overlayed reconnect requests");
+ thrownExceptions.add(e);
+ throw e;
+ }
+ try {
+ Thread.sleep(800); // simulating a workload
+ beingReconnected.set(false);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ @Override
+ protected void doStop() {
+ // empty
+ }
+ };
+
+ Runnable r = new Runnable() {
+ final private IOException e = new IOException("Forced reconnect");
+ @Override
+ public void run() {
+ try {
+ // forcing the reconnect request
+ cca.gotIOException(e);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ };
+
+ System.out.println(": Spawning " + THREAD_COUNT + " concurrent reconnect requests");
+
+ for(int i=0;i<THREAD_COUNT;i++) {
+ e.execute(r);
+ }
+
+ Thread.sleep(THREAD_COUNT * 1000);
+ e.shutdown();
+ e.awaitTermination(10, TimeUnit.SECONDS);
+
+ cca.terminate();
+
+ for(Exception thrown : thrownExceptions) {
+ throw thrown;
+ }
+ System.out.println(": Requests processed successfully");
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Client/Client.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,47 @@
+
+import java.nio.charset.Charset;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchService;
+import javax.management.MBeanServerConnection;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+public class Client {
+ public static void main(String[] argv) throws Exception {
+ if (argv.length != 1) throw new IllegalArgumentException("Expecting exactly one jmx url argument");
+
+ JMXServiceURL serverUrl = new JMXServiceURL(argv[0]);
+
+ ObjectName name = new ObjectName("test", "foo", "bar");
+ JMXConnector jmxConnector = JMXConnectorFactory.connect(serverUrl);
+ System.out.println("client connected");
+ jmxConnector.addConnectionNotificationListener(new NotificationListener() {
+ public void handleNotification(Notification notification, Object handback) {
+ System.err.println("no!" + notification);
+ }
+ }, null, null);
+ MBeanServerConnection jmxServer = jmxConnector.getMBeanServerConnection();
+
+ jmxServer.addNotificationListener(name, new NotificationListener() {
+ public void handleNotification(Notification notification, Object handback) {
+ System.out.println("client got:" + notification);
+ }
+ }, null, null);
+
+ for(int i=0;i<10;i++) {
+ System.out.println("client invoking foo");
+ jmxServer.invoke(name, "foo", new Object[]{}, new String[]{});
+ Thread.sleep(50);
+ }
+
+ System.err.println("happy!");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Client/ConfigKey.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,3 @@
+public enum ConfigKey {
+ CONSTANT3, CONSTANT2;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Client/TestNotification.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,25 @@
+
+import javax.management.Notification;
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ *
+ * @author Jaroslav Bachorik <jaroslav.bachorik at oracle.com>
+ */
+public class TestNotification extends Notification {
+ private ConfigKey key;
+
+ public TestNotification(String type, Object source, long sequenceNumber) {
+ super(type, source, sequenceNumber);
+ key = ConfigKey.CONSTANT3;
+ }
+
+ @Override
+ public String toString() {
+ return "TestNotification{" + "key=" + key + '}';
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Server/ConfigKey.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,3 @@
+public enum ConfigKey {
+ CONSTANT1, CONSTANT2;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Server/Server.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,46 @@
+import java.lang.management.ManagementFactory;
+import java.net.BindException;
+import java.nio.charset.Charset;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.server.ExportException;
+import java.util.Random;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+public class Server {
+ public static void main(String[] argv) throws Exception {
+ int serverPort = 12345;
+ ObjectName name = new ObjectName("test", "foo", "bar");
+ MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer();
+ SteMBean bean = new Ste();
+ jmxServer.registerMBean(bean, name);
+ boolean exported = false;
+ Random rnd = new Random(System.currentTimeMillis());
+ do {
+ try {
+ LocateRegistry.createRegistry(serverPort);
+ exported = true;
+ } catch (ExportException ee) {
+ if (ee.getCause() instanceof BindException) {
+ serverPort = rnd.nextInt(10000) + 4096;
+ } else {
+ throw ee;
+ }
+ }
+
+ } while (!exported);
+ JMXServiceURL serverUrl = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + serverPort + "/test");
+ JMXConnectorServer jmxConnector = JMXConnectorServerFactory.newJMXConnectorServer(serverUrl, null, jmxServer);
+ jmxConnector.start();
+ System.out.println(serverUrl);
+ System.err.println("server listening on " + serverUrl);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Server/Ste.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,10 @@
+
+import javax.management.NotificationBroadcasterSupport;
+
+public class Ste extends NotificationBroadcasterSupport implements SteMBean {
+ private long count = 0;
+
+ public void foo() {
+ sendNotification(new TestNotification("test", this, count++));
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Server/SteMBean.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,3 @@
+public interface SteMBean {
+ public void foo();
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Server/TestNotification.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,25 @@
+
+import javax.management.Notification;
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ *
+ * @author Jaroslav Bachorik <jaroslav.bachorik at oracle.com>
+ */
+public class TestNotification extends Notification {
+ private ConfigKey key;
+
+ public TestNotification(String type, Object source, long sequenceNumber) {
+ super(type, source, sequenceNumber);
+ key = ConfigKey.CONSTANT1;
+ }
+
+ @Override
+ public String toString() {
+ return "TestNotification{" + "key=" + key + '}';
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/TestSerializationMismatch.sh Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,93 @@
+#
+# Copyright (c) 2005, 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.
+#
+
+#
+# @test
+# @summary Tests for the RMI unmarshalling errors not to cause silent failure.
+# @author Jaroslav Bachorik
+# @bug 6937053
+#
+# @run shell TestSerializationMismatch.sh
+#
+
+#set -x
+
+#Set appropriate jdk
+#
+
+if [ ! -z "${TESTJAVA}" ] ; then
+ jdk="$TESTJAVA"
+else
+ echo "--Error: TESTJAVA must be defined as the pathname of a jdk to test."
+ exit 1
+fi
+
+SERVER_TESTCLASSES=$TESTCLASSES/Server
+CLIENT_TESTCLASSES=$TESTCLASSES/Client
+
+URL_PATH=$SERVER_TESTCLASSES/jmxurl
+
+rm $URL_PATH
+
+mkdir -p $SERVER_TESTCLASSES
+mkdir -p $CLIENT_TESTCLASSES
+
+$TESTJAVA/bin/javac -d $CLIENT_TESTCLASSES $TESTSRC/Client/ConfigKey.java $TESTSRC/Client/TestNotification.java $TESTSRC/Client/Client.java
+$TESTJAVA/bin/javac -d $SERVER_TESTCLASSES $TESTSRC/Server/ConfigKey.java $TESTSRC/Server/TestNotification.java $TESTSRC/Server/SteMBean.java $TESTSRC/Server/Ste.java $TESTSRC/Server/Server.java
+
+startServer()
+{
+ ($TESTJAVA/bin/java -classpath $SERVER_TESTCLASSES Server) 1>$URL_PATH &
+ SERVER_PID=$!
+}
+
+runClient()
+{
+ while true
+ do
+ [ -f $URL_PATH ] && break
+ sleep 2
+ done
+ read JMXURL < $URL_PATH
+
+ HAS_ERRORS=`($TESTJAVA/bin/java -classpath $CLIENT_TESTCLASSES Client $JMXURL) 2>&1 | grep -i "SEVERE: Failed to fetch notification, stopping thread. Error is: java.rmi.UnmarshalException"`
+}
+
+startServer
+
+runClient
+
+sleep 1 # wait for notifications to arrive
+
+kill "$SERVER_PID"
+
+if [ -z "$HAS_ERRORS" ]
+then
+ echo "Test PASSED"
+ exit 0
+fi
+
+echo "Test FAILED"
+echo $HAS_ERRORS 1>&2
+exit 1
+
--- a/jdk/test/java/io/Serializable/resolveProxyClass/NonPublicInterface.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/io/Serializable/resolveProxyClass/NonPublicInterface.java Fri Dec 28 18:36:41 2012 -0800
@@ -22,14 +22,17 @@
*/
/* @test
- * @bug 4413817
+ * @bug 4413817 8004928
* @summary Verify that ObjectInputStream.resolveProxyClass can properly
* resolve a dynamic proxy class which implements a non-public
* interface not defined in the latest user defined class loader.
*/
import java.io.*;
-import java.lang.reflect.*;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
public class NonPublicInterface {
@@ -39,35 +42,19 @@
}
}
+ public static final String nonPublicIntrfaceName = "java.util.zip.ZipConstants";
+
public static void main(String[] args) throws Exception {
- Class nonPublic = null;
- String[] nonPublicInterfaces = new String[] {
- "java.awt.Conditional",
- "java.util.zip.ZipConstants",
- "javax.swing.GraphicsWrapper",
- "javax.swing.JPopupMenu$Popup",
- "javax.swing.JTable$Resizable2",
- "javax.swing.JTable$Resizable3",
- "javax.swing.ToolTipManager$Popup",
- "sun.audio.Format",
- "sun.audio.HaePlayable",
- "sun.tools.agent.StepConstants",
- };
- for (int i = 0; i < nonPublicInterfaces.length; i++) {
- try {
- nonPublic = Class.forName(nonPublicInterfaces[i]);
- break;
- } catch (ClassNotFoundException ex) {
- }
- }
- if (nonPublic == null) {
- throw new Error("couldn't find system non-public interface");
+ Class<?> nonPublic = Class.forName(nonPublicIntrfaceName);
+ if (Modifier.isPublic(nonPublic.getModifiers())) {
+ throw new Error("Interface " + nonPublicIntrfaceName +
+ " is public and need to be changed!");
}
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(bout);
oout.writeObject(Proxy.newProxyInstance(nonPublic.getClassLoader(),
- new Class[]{ nonPublic }, new Handler()));
+ new Class<?>[]{ nonPublic }, new Handler()));
oout.close();
ObjectInputStream oin = new ObjectInputStream(
new ByteArrayInputStream(bout.toByteArray()));
--- a/jdk/test/java/lang/Throwable/LegacyChainedExceptionSerialization.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/lang/Throwable/LegacyChainedExceptionSerialization.java Fri Dec 28 18:36:41 2012 -0800
@@ -25,7 +25,7 @@
/**
* @test
- * @bug 4385429
+ * @bug 4385429 8004928
* @summary Certain legacy chained exceptions throw IllegalArgumentException
* upon deserialization if "causative exception" is null.
* @author Josh Bloch
@@ -36,8 +36,7 @@
new ExceptionInInitializerError(),
new java.lang.reflect.UndeclaredThrowableException(null),
new java.lang.reflect.InvocationTargetException(null),
- new java.security.PrivilegedActionException(null),
- new java.awt.print.PrinterIOException(null)
+ new java.security.PrivilegedActionException(null)
};
public static void main(String[] args) throws Exception {
--- a/jdk/test/java/lang/management/CompilationMXBean/Basic.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/lang/management/CompilationMXBean/Basic.java Fri Dec 28 18:36:41 2012 -0800
@@ -23,10 +23,10 @@
/*
* @test
- * @bug 5011189
+ * @bug 5011189 8004928
* @summary Unit test for java.lang.management.CompilationMXBean
*
- * @run main/othervm -Xcomp -Xbatch -Djava.awt.headless=true Basic
+ * @run main/othervm -Xcomp -Xbatch Basic
*/
import java.lang.management.*;
@@ -65,8 +65,6 @@
java.util.Locale.getAvailableLocales();
java.security.Security.getProviders();
- java.awt.Toolkit.getDefaultToolkit();
- javax.swing.UIManager.getInstalledLookAndFeels();
java.nio.channels.spi.SelectorProvider.provider();
time = mb.getTotalCompilationTime();
--- a/jdk/test/java/lang/reflect/Generics/Probe.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/lang/reflect/Generics/Probe.java Fri Dec 28 18:36:41 2012 -0800
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 5003916 6704655 6873951 6476261
+ * @bug 5003916 6704655 6873951 6476261 8004928
* @summary Testing parsing of signatures attributes of nested classes
* @author Joseph D. Darcy
*/
@@ -52,8 +52,7 @@
"java.util.HashMap$ValueIterator",
"java.util.LinkedHashMap$EntryIterator",
"java.util.LinkedHashMap$KeyIterator",
- "java.util.LinkedHashMap$ValueIterator",
- "javax.swing.JComboBox$AccessibleJComboBox"})
+ "java.util.LinkedHashMap$ValueIterator"})
public class Probe {
public static void main (String... args) throws Throwable {
Classes classesAnnotation = (Probe.class).getAnnotation(Classes.class);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/reflect/Method/IsDefaultTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2012 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.
+ */
+
+/*
+ * @test
+ * @bug 8005042
+ * @summary Check behavior of Method.isDefault
+ * @author Joseph D. Darcy
+ */
+
+import java.lang.reflect.*;
+import java.lang.annotation.*;
+import java.util.*;
+
+public class IsDefaultTest {
+ public static void main(String... argv) throws Exception {
+ int failures = 0;
+ int visitationCount = 0;
+
+ List<Class<?>> classList = new ArrayList<>();
+ classList.add(TestType1.class);
+ classList.add(TestType2.class);
+ classList.add(TestType3.class);
+ classList.add(TestType4.class);
+
+ for(Class<?> clazz: classList) {
+ for(Method method: clazz.getDeclaredMethods()) {
+ ExpectedIsDefault expectedIsDefault = method.getAnnotation(ExpectedIsDefault.class);
+ if (expectedIsDefault != null) {
+ visitationCount++;
+ boolean expected = expectedIsDefault.value();
+ boolean actual = method.isDefault();
+
+ if (actual != expected) {
+ failures++;
+ System.err.printf("ERROR: On %s expected isDefault of ''%s''; got ''%s''.\n",
+ method.toString(), expected, actual);
+ }
+ }
+ }
+ }
+
+ if (visitationCount == 0) {
+ System.err.println("Test failed because no methods checked.");
+ throw new RuntimeException();
+ }
+
+ if (failures > 0) {
+ System.err.println("Test failed.");
+ throw new RuntimeException();
+ }
+ }
+}
+
+interface TestType1 {
+ @ExpectedIsDefault(false)
+ void foo();
+
+ @ExpectedIsDefault(true)
+ default void bar() {}; // Default method
+}
+
+class TestType2 {
+ @ExpectedIsDefault(false)
+ void bar() {};
+}
+
+class TestType3 implements TestType1 {
+ @ExpectedIsDefault(false)
+ public void foo(){}
+
+ @ExpectedIsDefault(false)
+ @Override
+ public void bar() {};
+}
+
+@interface TestType4 {
+ @ExpectedIsDefault(false)
+ String value();
+
+ @ExpectedIsDefault(false)
+ String anotherValue() default "";
+}
+
+@Retention(RetentionPolicy.RUNTIME)
+@interface ExpectedIsDefault {
+ boolean value();
+}
--- a/jdk/test/java/lang/reflect/Proxy/ClassRestrictions.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/lang/reflect/Proxy/ClassRestrictions.java Fri Dec 28 18:36:41 2012 -0800
@@ -22,7 +22,7 @@
*/
/* @test
- * @bug 4227192
+ * @bug 4227192 8004928
* @summary This is a test of the restrictions on the parameters that may
* be passed to the Proxy.getProxyClass method.
* @author Peter Jones
@@ -31,6 +31,7 @@
* @run main ClassRestrictions
*/
+import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.net.URLClassLoader;
@@ -48,6 +49,8 @@
void foo();
}
+ public static final String nonPublicIntrfaceName = "java.util.zip.ZipConstants";
+
public static void main(String[] args) {
System.err.println(
@@ -65,7 +68,7 @@
try {
interfaces = new Class<?>[] { Object.class };
proxyClass = Proxy.getProxyClass(loader, interfaces);
- throw new RuntimeException(
+ throw new Error(
"proxy class created with java.lang.Object as interface");
} catch (IllegalArgumentException e) {
e.printStackTrace();
@@ -75,7 +78,7 @@
try {
interfaces = new Class<?>[] { Integer.TYPE };
proxyClass = Proxy.getProxyClass(loader, interfaces);
- throw new RuntimeException(
+ throw new Error(
"proxy class created with int.class as interface");
} catch (IllegalArgumentException e) {
e.printStackTrace();
@@ -90,7 +93,7 @@
try {
interfaces = new Class<?>[] { Bar.class, Bar.class };
proxyClass = Proxy.getProxyClass(loader, interfaces);
- throw new RuntimeException(
+ throw new Error(
"proxy class created with repeated interfaces");
} catch (IllegalArgumentException e) {
e.printStackTrace();
@@ -109,7 +112,7 @@
try {
interfaces = new Class<?>[] { altBarClass };
proxyClass = Proxy.getProxyClass(loader, interfaces);
- throw new RuntimeException(
+ throw new Error(
"proxy class created with interface " +
"not visible to class loader");
} catch (IllegalArgumentException e) {
@@ -122,34 +125,16 @@
* All non-public interfaces must be in the same package.
*/
Class<?> nonPublic1 = Bashful.class;
- Class<?> nonPublic2 = null;
- String[] nonPublicInterfaces = new String[] {
- "java.awt.Conditional",
- "java.util.zip.ZipConstants",
- "javax.swing.GraphicsWrapper",
- "javax.swing.JPopupMenu$Popup",
- "javax.swing.JTable$Resizable2",
- "javax.swing.JTable$Resizable3",
- "javax.swing.ToolTipManager$Popup",
- "sun.audio.Format",
- "sun.audio.HaePlayable",
- "sun.tools.agent.StepConstants",
- };
- for (int i = 0; i < nonPublicInterfaces.length; i++) {
- try {
- nonPublic2 = Class.forName(nonPublicInterfaces[i]);
- break;
- } catch (ClassNotFoundException e) {
- }
- }
- if (nonPublic2 == null) {
- throw new RuntimeException(
- "no second non-public interface found for test");
+ Class<?> nonPublic2 = Class.forName(nonPublicIntrfaceName);
+ if (Modifier.isPublic(nonPublic2.getModifiers())) {
+ throw new Error(
+ "Interface " + nonPublicIntrfaceName +
+ " is public and need to be changed!");
}
try {
interfaces = new Class<?>[] { nonPublic1, nonPublic2 };
proxyClass = Proxy.getProxyClass(loader, interfaces);
- throw new RuntimeException(
+ throw new Error(
"proxy class created with two non-public interfaces " +
"in different packages");
} catch (IllegalArgumentException e) {
@@ -165,7 +150,7 @@
try {
interfaces = new Class<?>[] { Bar.class, Baz.class };
proxyClass = Proxy.getProxyClass(loader, interfaces);
- throw new RuntimeException(
+ throw new Error(
"proxy class created with conflicting methods");
} catch (IllegalArgumentException e) {
e.printStackTrace();
@@ -178,10 +163,10 @@
*/
System.err.println("\nTEST PASSED");
- } catch (Exception e) {
+ } catch (Throwable e) {
System.err.println("\nTEST FAILED:");
e.printStackTrace();
- throw new RuntimeException("TEST FAILED: " + e.toString());
+ throw new Error("TEST FAILED: ", e);
}
}
}
--- a/jdk/test/java/rmi/testlibrary/JavaVM.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/rmi/testlibrary/JavaVM.java Fri Dec 28 18:36:41 2012 -0800
@@ -21,13 +21,10 @@
* questions.
*/
-/**
- *
- */
-
-import java.io.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
import java.util.Arrays;
-import java.util.Properties;
import java.util.StringTokenizer;
/**
@@ -45,10 +42,6 @@
private OutputStream errorStream = System.err;
private String policyFileName = null;
- // This is used to shorten waiting time at startup.
- private volatile boolean started = false;
- private boolean forcesOutput = true; // default behavior
-
private static void mesg(Object mesg) {
System.err.println("JAVAVM: " + mesg.toString());
}
@@ -82,24 +75,6 @@
this.errorStream = err;
}
- /* This constructor will instantiate a JavaVM object for which caller
- * can ask for forcing initial version output on child vm process
- * (if forcesVersionOutput is true), or letting the started vm behave freely
- * (when forcesVersionOutput is false).
- */
- public JavaVM(String classname,
- String options, String args,
- OutputStream out, OutputStream err,
- boolean forcesVersionOutput) {
- this(classname, options, args, out, err);
- this.forcesOutput = forcesVersionOutput;
- }
-
-
- public void setStarted() {
- started = true;
- }
-
// Prepends passed opts array to current options
public void addOptions(String[] opts) {
String newOpts = "";
@@ -139,7 +114,8 @@
*/
public void start() throws IOException {
- if (vm != null) return;
+ if (vm != null)
+ throw new IllegalStateException("JavaVM already started");
/*
* If specified, add option for policy file
@@ -151,18 +127,6 @@
addOptions(new String[] { getCodeCoverageOptions() });
- /*
- * If forcesOutput is true :
- * We force the new starting vm to output something so that we can know
- * when it is effectively started by redirecting standard output through
- * the next StreamPipe call (the vm is considered started when a first
- * output has been streamed out).
- * We do this by prepnding a "-showversion" option in the command line.
- */
- if (forcesOutput) {
- addOptions(new String[] {"-showversion"});
- }
-
StringTokenizer optionsTokenizer = new StringTokenizer(options);
StringTokenizer argsTokenizer = new StringTokenizer(args);
int optionsCount = optionsTokenizer.countTokens();
@@ -186,43 +150,8 @@
vm = Runtime.getRuntime().exec(javaCommand);
/* output from the execed process may optionally be captured. */
- StreamPipe.plugTogether(this, vm.getInputStream(), this.outputStream);
- StreamPipe.plugTogether(this, vm.getErrorStream(), this.errorStream);
-
- try {
- if (forcesOutput) {
- // Wait distant vm to start, by using waiting time slices of 100 ms.
- // Wait at most for 2secs, after it considers the vm to be started.
- final long vmStartSleepTime = 100;
- final int maxTrials = 20;
- int numTrials = 0;
- while (!started && numTrials < maxTrials) {
- numTrials++;
- Thread.sleep(vmStartSleepTime);
- }
-
- // Outputs running status of distant vm
- String message =
- "after " + (numTrials * vmStartSleepTime) + " milliseconds";
- if (started) {
- mesg("distant vm process running, " + message);
- }
- else {
- mesg("unknown running status of distant vm process, " + message);
- }
- }
- else {
- // Since we have no way to know if the distant vm is started,
- // we just consider the vm to be started after a 2secs waiting time.
- Thread.sleep(2000);
- mesg("distant vm considered to be started after a waiting time of 2 secs");
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- mesg("Thread interrupted while checking if distant vm is started. Giving up check.");
- mesg("Distant vm state unknown");
- return;
- }
+ StreamPipe.plugTogether(vm.getInputStream(), this.outputStream);
+ StreamPipe.plugTogether(vm.getErrorStream(), this.errorStream);
}
public void destroy() {
--- a/jdk/test/java/rmi/testlibrary/StreamPipe.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/rmi/testlibrary/StreamPipe.java Fri Dec 28 18:36:41 2012 -0800
@@ -21,11 +21,10 @@
* questions.
*/
-/**
- *
- */
-
-import java.io.*;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.IOException;
+import java.io.OutputStream;
/**
* Pipe output of one stream into input of another.
@@ -34,84 +33,46 @@
private InputStream in;
private OutputStream out;
- private String preamble;
- private JavaVM javaVM;
- private static Object lock = new Object();
+
+ private static Object countLock = new Object();
private static int count = 0;
-
- /* StreamPipe constructor : should only be called by plugTogether() method !!
- * If passed vm is not null :
- * - This is StreamPipe usage when streams to pipe come from a given
- * vm (JavaVM) process (the vm process must be started with a prefixed
- * "-showversion" option to be able to determine as soon as possible when
- * the vm process is started through the redirection of the streams).
- * There must be a close connection between the StreamPipe instance and
- * the JavaVM object on which a start() call has been done.
- * run() method will flag distant JavaVM as started.
- * If passed vm is null :
- * - We don't have control on the process which we want to redirect the passed
- * streams.
- * run() method will ignore distant process.
+ /**
+ * StreamPipe constructor : should only be called by plugTogether() method.
*/
- private StreamPipe(JavaVM vm, InputStream in, OutputStream out, String name) {
+ private StreamPipe(InputStream in, OutputStream out, String name) {
super(name);
this.in = in;
this.out = out;
- this.preamble = "# ";
- this.javaVM = vm;
}
- // Install redirection of passed InputStream and OutputStream from passed JavaVM
- // to this vm standard output and input streams.
- public static void plugTogether(JavaVM vm, InputStream in, OutputStream out) {
- String name = null;
+ /**
+ * Creates a StreamPipe thread that copies in to out and returns
+ * the created instance.
+ */
+ public static StreamPipe plugTogether(InputStream in, OutputStream out) {
+ String name;
- synchronized (lock) {
- name = "TestLibrary: StreamPipe-" + (count ++ );
+ synchronized (countLock) {
+ name = "java.rmi.testlibrary.StreamPipe-" + (count++);
}
- Thread pipe = new StreamPipe(vm, in, out, name);
+ StreamPipe pipe = new StreamPipe(in, out, name);
pipe.setDaemon(true);
pipe.start();
- }
-
- /* Redirects the InputStream and OutputStream passed by caller to this
- * vm standard output and input streams.
- * (we just have to use fully parametered plugTogether() call with a null
- * JavaVM input to do this).
- */
- public static void plugTogether(InputStream in, OutputStream out) {
- plugTogether(null, in, out);
+ return pipe;
}
// Starts redirection of streams.
public void run() {
- BufferedReader r = new BufferedReader(new InputStreamReader(in), 1);
- BufferedWriter w = new BufferedWriter(new OutputStreamWriter(out));
- byte[] buf = new byte[256];
-
try {
- String line;
+ byte[] buf = new byte[1024];
- /* This is to check that the distant vm has started,
- * if such a vm has been provided at construction :
- * - As soon as we can read something from r BufferedReader,
- * that means the distant vm is already started.
- * Thus we signal associated JavaVM object that it is now started.
- */
- if (((line = r.readLine()) != null) &&
- (javaVM != null)) {
- javaVM.setStarted();
- }
-
- // Redirects r on w.
- while (line != null) {
- w.write(preamble);
- w.write(line);
- w.newLine();
- w.flush();
- line = r.readLine();
+ while (true) {
+ int nr = in.read(buf);
+ if (nr == -1)
+ break;
+ out.write(buf, 0, nr);
}
} catch (InterruptedIOException iioe) {
// Thread interrupted during IO operation. Terminate StreamPipe.
@@ -121,5 +82,4 @@
e.printStackTrace();
}
}
-
}
--- a/jdk/test/java/security/cert/CertPathBuilder/targetConstraints/BuildEEBasicConstraints.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/security/cert/CertPathBuilder/targetConstraints/BuildEEBasicConstraints.java Fri Dec 28 18:36:41 2012 -0800
@@ -21,18 +21,22 @@
* questions.
*/
+// This test case relies on updated static security property, no way to re-use
+// security property in samevm/agentvm mode.
+
/**
* @test
* @bug 6714842
* @library ../../../testlibrary
* @build CertUtils
- * @run main BuildEEBasicConstraints
+ * @run main/othervm BuildEEBasicConstraints
* @summary make sure a PKIX CertPathBuilder builds a path to an
* end entity certificate when the setBasicConstraints method of the
* X509CertSelector of the targetConstraints PKIXBuilderParameters
* parameter is set to -2.
*/
+import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertPath;
import java.security.cert.CertStore;
@@ -49,6 +53,9 @@
public final class BuildEEBasicConstraints {
public static void main(String[] args) throws Exception {
+ // reset the security property to make sure that the algorithms
+ // and keys used in this test are not disabled.
+ Security.setProperty("jdk.certpath.disabledAlgorithms", "MD2");
X509Certificate rootCert = CertUtils.getCertFromFile("anchor.cer");
TrustAnchor anchor = new TrustAnchor
--- a/jdk/test/java/security/cert/pkix/policyChanges/TestPolicy.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/security/cert/pkix/policyChanges/TestPolicy.java Fri Dec 28 18:36:41 2012 -0800
@@ -21,16 +21,22 @@
* questions.
*/
+// This test case relies on updated static security property, no way to re-use
+// security property in samevm/agentvm mode.
+
/**
* @test
* @bug 4684793
- * @summary verify that the RFC3280 policy processing changes are implemented correctly
+ * @summary verify that the RFC3280 policy processing changes are
+ * implemented correctly
+ * @run main/othervm TestPolicy
* @author Andreas Sterbenz
*/
import java.io.*;
import java.util.*;
+import java.security.Security;
import java.security.cert.*;
public class TestPolicy {
@@ -72,6 +78,10 @@
};
public static void main(String[] args) throws Exception {
+ // reset the security property to make sure that the algorithms
+ // and keys used in this test are not disabled.
+ Security.setProperty("jdk.certpath.disabledAlgorithms", "MD2");
+
factory = CertificateFactory.getInstance("X.509");
X509Certificate anchor = loadCertificate("anchor.cer");
--- a/jdk/test/java/text/Bidi/BidiConformance.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/text/Bidi/BidiConformance.java Fri Dec 28 18:36:41 2012 -0800
@@ -656,7 +656,6 @@
baseIsLTR4Constructor1[textNo][testNo],
isLTR_isRTL4Constructor1[textNo][0][testNo],
isLTR_isRTL4Constructor1[textNo][1][testNo]);
-System.out.println(bidi.toString());
}
private void callTestEachMethod4Constructor2(int textNo,
@@ -668,7 +667,6 @@
baseIsLTR4Constructor2[textNo][flagNo],
isLTR_isRTL4Constructor2[textNo][0][flagNo],
isLTR_isRTL4Constructor2[textNo][1][flagNo]);
-System.out.println(bidi.toString());
}
private void callTestEachMethod4Constructor3(int textNo,
@@ -680,7 +678,6 @@
baseIsLTR4Constructor3[textNo][dataNo],
isLTR_isRTL4Constructor3[textNo][0][dataNo],
isLTR_isRTL4Constructor3[textNo][1][dataNo]);
-System.out.println(bidi.toString());
}
private StringBuilder sb = new StringBuilder();
@@ -934,59 +931,145 @@
System.out.println("*** Test getRunLevel()");
String str = "ABC 123";
- int length = str.length();
Bidi bidi = new Bidi(str, Bidi.DIRECTION_LEFT_TO_RIGHT);
-
try {
- if (bidi.getRunLevel(-1) != 0 || // runCount - 2
+ if (bidi.getRunLevel(-1) != 0 || // runCount - 2 (out of range)
bidi.getRunLevel(0) != 0 || // runCount - 1
- bidi.getRunLevel(1) != 0 || // runCount
- bidi.getRunLevel(2) != 0) { // runCount + 1
- errorHandling("getRunLevel() should return 0" +
- " when getRunCount() is 1.");
+ bidi.getRunLevel(1) != 0 || // runCount (out of range)
+ bidi.getRunLevel(2) != 0) { // runCount + 1 (out of range)
+ errorHandling("Incorrect getRunLevel() value(s).");
}
}
catch (Exception e) {
- errorHandling("getRunLevel() should not throw an exception " +
- "when getRunCount() is 1.");
+ errorHandling("getRunLevel() should not throw an exception: " + e);
}
str = "ABC " + HebrewABC + " 123";
- length = str.length();
bidi = new Bidi(str, Bidi.DIRECTION_LEFT_TO_RIGHT);
+ try {
+ if (bidi.getRunLevel(-1) != 0 || // runCount - 4 (out of range)
+ bidi.getRunLevel(0) != 0 || // runCount - 3
+ bidi.getRunLevel(1) != 1 || // runCount - 2
+ bidi.getRunLevel(2) != 2 || // runCount - 1
+ bidi.getRunLevel(3) != 0 || // runCount (out of range)
+ bidi.getRunLevel(4) != 0) { // runCount + 1 (out of range)
+ errorHandling("Incorrect getRunLevel() value(s).");
+ }
+ }
+ catch (Exception e) {
+ errorHandling("getRunLevel() should not throw an exception: " + e);
+ }
+ str = "ABC";
+ bidi = new Bidi(str, Bidi.DIRECTION_LEFT_TO_RIGHT);
try {
- bidi.getRunLevel(-1);
- errorHandling("getRunLevel() should throw an AIOoBE " +
- "when run is -1(too small).");
+ if (bidi.getRunLevel(-1) != 0 || // runCount - 2 (out of range)
+ bidi.getRunLevel(0) != 0 || // runCount - 1
+ bidi.getRunLevel(1) != 0 || // runCount (out of range)
+ bidi.getRunLevel(2) != 0) { // runCount + 1 (out of range)
+ errorHandling("Incorrect getRunLevel() value(s).");
+ }
+ }
+ catch (Exception e) {
+ errorHandling("getRunLevel() should not throw an exception: " + e);
}
- catch (ArrayIndexOutOfBoundsException e) {
+
+ str = "ABC";
+ bidi = new Bidi(str, Bidi.DIRECTION_RIGHT_TO_LEFT);
+ try {
+ if (bidi.getRunLevel(-1) != 1 || // runCount - 2 (out of range)
+ bidi.getRunLevel(0) != 2 || // runCount - 1
+ bidi.getRunLevel(1) != 1 || // runCount (out of range)
+ bidi.getRunLevel(2) != 1) { // runCount + 1 (out of range)
+ errorHandling("Incorrect getRunLevel() value(s).");
+ }
+ }
+ catch (Exception e) {
+ errorHandling("getRunLevel() should not throw an exception: " + e);
}
- catch (IllegalArgumentException e) {
- errorHandling("getRunLevel() should not throw an IAE " +
- "but an AIOoBE when run is -1(too small).");
+
+ str = "ABC";
+ bidi = new Bidi(str, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT);
+ try {
+ if (bidi.getRunLevel(-1) != 0 || // runCount - 2 (out of range)
+ bidi.getRunLevel(0) != 0 || // runCount - 1
+ bidi.getRunLevel(1) != 0 || // runCount (out of range)
+ bidi.getRunLevel(2) != 0) { // runCount + 1 (out of range)
+ errorHandling("Incorrect getRunLevel() value(s).");
+ }
+ }
+ catch (Exception e) {
+ errorHandling("getRunLevel() should not throw an exception: " + e);
}
+ str = "ABC";
+ bidi = new Bidi(str, Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT);
try {
- bidi.getRunLevel(0);
- bidi.getRunLevel(1);
- bidi.getRunLevel(2);
+ if (bidi.getRunLevel(-1) != 0 || // runCount - 2 (out of range)
+ bidi.getRunLevel(0) != 0 || // runCount - 1
+ bidi.getRunLevel(1) != 0 || // runCount (out of range)
+ bidi.getRunLevel(2) != 0) { // runCount + 1 (out of range)
+ errorHandling("Incorrect getRunLevel() value(s).");
+ }
}
catch (Exception e) {
- errorHandling("getRunLevel() should not throw an exception" +
- " when run is from 0 to 2(runCount-1).");
+ errorHandling("getRunLevel() should not throw an exception: " + e);
+ }
+
+ str = HebrewABC;
+ bidi = new Bidi(str, Bidi.DIRECTION_LEFT_TO_RIGHT);
+ try {
+ if (bidi.getRunLevel(-1) != 0 || // runCount - 2 (out of range)
+ bidi.getRunLevel(0) != 1 || // runCount - 1
+ bidi.getRunLevel(1) != 0 || // runCount (out of range)
+ bidi.getRunLevel(2) != 0) { // runCount + 1 (out of range)
+ errorHandling("Incorrect getRunLevel() value(s).");
+ }
+ }
+ catch (Exception e) {
+ errorHandling("getRunLevel() should not throw an exception: " + e);
}
+ str = HebrewABC;
+ bidi = new Bidi(str, Bidi.DIRECTION_RIGHT_TO_LEFT);
try {
- bidi.getRunLevel(3);
- errorHandling("getRunLevel() should throw an AIOoBE" +
- " when run is 3(same as runCount).");
+ if (bidi.getRunLevel(-1) != 1 || // runCount - 2 (out of range)
+ bidi.getRunLevel(0) != 1 || // runCount - 1
+ bidi.getRunLevel(1) != 1 || // runCount (out of range)
+ bidi.getRunLevel(2) != 1) { // runCount + 1 (out of range)
+ errorHandling("Incorrect getRunLevel() value(s).");
+ }
+ }
+ catch (Exception e) {
+ errorHandling("getRunLevel() should not throw an exception: " + e);
}
- catch (ArrayIndexOutOfBoundsException e) {
+
+ str = HebrewABC;
+ bidi = new Bidi(str, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT);
+ try {
+ if (bidi.getRunLevel(-1) != 1 || // runCount - 2 (out of range)
+ bidi.getRunLevel(0) != 1 || // runCount - 1
+ bidi.getRunLevel(1) != 1 || // runCount (out of range)
+ bidi.getRunLevel(2) != 1) { // runCount + 1 (out of range)
+ errorHandling("Incorrect getRunLevel() value(s).");
+ }
+ }
+ catch (Exception e) {
+ errorHandling("getRunLevel() should not throw an exception: " + e);
}
- catch (IllegalArgumentException e) {
- errorHandling("getRunLevel() should not throw an IAE " +
- "but an AIOoBE when run is 3(same as runCount).");
+
+ str = HebrewABC;
+ bidi = new Bidi(str, Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT);
+ try {
+ if (bidi.getRunLevel(-1) != 1 || // runCount - 2 (out of range)
+ bidi.getRunLevel(0) != 1 || // runCount - 1
+ bidi.getRunLevel(1) != 1 || // runCount (out of range)
+ bidi.getRunLevel(2) != 1) { // runCount + 1 (out of range)
+ errorHandling("Incorrect getRunLevel() value(s).");
+ }
+ }
+ catch (Exception e) {
+ errorHandling("getRunLevel() should not throw an exception: " + e);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/text/Bidi/Bug8005277.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/*
+ * @test
+ * @bug 8005277
+ * @summary verify that Bidi.getRunLevel() returns a corect level.
+ */
+import java.text.Bidi;
+
+public class Bug8005277 {
+
+ public static void main(String[] args) {
+ boolean err = false;
+ String string = "\u05D0\u05D1\u05D2";
+ Bidi bidi = new Bidi(string, Bidi.DIRECTION_LEFT_TO_RIGHT);
+
+ int result = bidi.getRunCount();
+ if (result != 1) {
+ System.err.println("Incorrect run count: " + result);
+ err = true;
+ }
+
+ result = bidi.getRunStart(0);
+ if (result != 0) {
+ System.err.println("Incorrect run start: " + result);
+ err = true;
+ }
+
+ result = bidi.getRunLimit(0);
+ if (result != 3) {
+ System.err.println("Incorrect run limit: " + result);
+ err = true;
+ }
+
+ result = bidi.getRunLevel(0);
+ if (result != 1) {
+ System.err.println("Incorrect run level: " + result);
+ err = true;
+ }
+
+ if (err) {
+ throw new RuntimeException("Failed.");
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Arrays/ParallelSorting.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,2067 @@
+/*
+ * Copyright (c) 2011, 2012, 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.
+ */
+
+/* Adapted from test/java/util/Arrays/Sorting.java
+ *
+ * Where that test checks Arrays.sort against manual quicksort routines,
+ * this test checks parallelSort against either Arrays.sort or manual
+ * quicksort routines.
+ */
+
+/*
+ * @test
+ * @bug 8003981
+ * @run main ParallelSorting -shortrun
+ * @summary Exercise Arrays.parallelSort (adapted from test Sorting)
+ *
+ * @author Vladimir Yaroslavskiy
+ * @author Jon Bentley
+ * @author Josh Bloch
+ */
+
+import java.util.Arrays;
+import java.util.Random;
+import java.io.PrintStream;
+import java.util.Comparator;
+
+public class ParallelSorting {
+ private static final PrintStream out = System.out;
+ private static final PrintStream err = System.err;
+
+ // Array lengths used in a long run (default)
+ private static final int[] LONG_RUN_LENGTHS = {
+ 1, 2, 3, 5, 8, 13, 21, 34, 55, 100, 1000, 10000, 100000, 1000000 };
+
+ // Array lengths used in a short run
+ private static final int[] SHORT_RUN_LENGTHS = {
+ 1, 2, 3, 21, 55, 1000, 10000 };
+
+ // Random initial values used in a long run (default)
+ private static final long[] LONG_RUN_RANDOMS = { 666, 0xC0FFEE, 999 };
+
+ // Random initial values used in a short run
+ private static final long[] SHORT_RUN_RANDOMS = { 666 };
+
+ public static void main(String[] args) {
+ boolean shortRun = args.length > 0 && args[0].equals("-shortrun");
+ long start = System.currentTimeMillis();
+
+ if (shortRun) {
+ testAndCheck(SHORT_RUN_LENGTHS, SHORT_RUN_RANDOMS);
+ } else {
+ testAndCheck(LONG_RUN_LENGTHS, LONG_RUN_RANDOMS);
+ }
+ long end = System.currentTimeMillis();
+
+ out.format("PASSED in %d sec.\n", Math.round((end - start) / 1E3));
+ }
+
+ private static void testAndCheck(int[] lengths, long[] randoms) {
+ testEmptyAndNullIntArray();
+ testEmptyAndNullLongArray();
+ testEmptyAndNullShortArray();
+ testEmptyAndNullCharArray();
+ testEmptyAndNullByteArray();
+ testEmptyAndNullFloatArray();
+ testEmptyAndNullDoubleArray();
+
+ for (int length : lengths) {
+ testMergeSort(length);
+ testAndCheckRange(length);
+ testAndCheckSubArray(length);
+ }
+ for (long seed : randoms) {
+ for (int length : lengths) {
+ testAndCheckWithInsertionSort(length, new MyRandom(seed));
+ testAndCheckWithCheckSum(length, new MyRandom(seed));
+ testAndCheckWithScrambling(length, new MyRandom(seed));
+ testAndCheckFloat(length, new MyRandom(seed));
+ testAndCheckDouble(length, new MyRandom(seed));
+ testStable(length, new MyRandom(seed));
+ }
+ }
+ }
+
+ private static void testEmptyAndNullIntArray() {
+ ourDescription = "Check empty and null array";
+ Arrays.parallelSort(new int[]{});
+ Arrays.parallelSort(new int[]{}, 0, 0);
+
+ try {
+ Arrays.parallelSort((int[]) null);
+ } catch (NullPointerException expected) {
+ try {
+ Arrays.parallelSort((int[]) null, 0, 0);
+ } catch (NullPointerException expected2) {
+ return;
+ }
+ failed("Arrays.parallelSort(int[],fromIndex,toIndex) shouldn't " +
+ "catch null array");
+ }
+ failed("Arrays.parallelSort(int[]) shouldn't catch null array");
+ }
+
+ private static void testEmptyAndNullLongArray() {
+ ourDescription = "Check empty and null array";
+ Arrays.parallelSort(new long[]{});
+ Arrays.parallelSort(new long[]{}, 0, 0);
+
+ try {
+ Arrays.parallelSort((long[]) null);
+ } catch (NullPointerException expected) {
+ try {
+ Arrays.parallelSort((long[]) null, 0, 0);
+ } catch (NullPointerException expected2) {
+ return;
+ }
+ failed("Arrays.parallelSort(long[],fromIndex,toIndex) shouldn't " +
+ "catch null array");
+ }
+ failed("Arrays.parallelSort(long[]) shouldn't catch null array");
+ }
+
+ private static void testEmptyAndNullShortArray() {
+ ourDescription = "Check empty and null array";
+ Arrays.parallelSort(new short[]{});
+ Arrays.parallelSort(new short[]{}, 0, 0);
+
+ try {
+ Arrays.parallelSort((short[]) null);
+ } catch (NullPointerException expected) {
+ try {
+ Arrays.parallelSort((short[]) null, 0, 0);
+ } catch (NullPointerException expected2) {
+ return;
+ }
+ failed("Arrays.parallelSort(short[],fromIndex,toIndex) shouldn't " +
+ "catch null array");
+ }
+ failed("Arrays.parallelSort(short[]) shouldn't catch null array");
+ }
+
+ private static void testEmptyAndNullCharArray() {
+ ourDescription = "Check empty and null array";
+ Arrays.parallelSort(new char[]{});
+ Arrays.parallelSort(new char[]{}, 0, 0);
+
+ try {
+ Arrays.parallelSort((char[]) null);
+ } catch (NullPointerException expected) {
+ try {
+ Arrays.parallelSort((char[]) null, 0, 0);
+ } catch (NullPointerException expected2) {
+ return;
+ }
+ failed("Arrays.parallelSort(char[],fromIndex,toIndex) shouldn't " +
+ "catch null array");
+ }
+ failed("Arrays.parallelSort(char[]) shouldn't catch null array");
+ }
+
+ private static void testEmptyAndNullByteArray() {
+ ourDescription = "Check empty and null array";
+ Arrays.parallelSort(new byte[]{});
+ Arrays.parallelSort(new byte[]{}, 0, 0);
+
+ try {
+ Arrays.parallelSort((byte[]) null);
+ } catch (NullPointerException expected) {
+ try {
+ Arrays.parallelSort((byte[]) null, 0, 0);
+ } catch (NullPointerException expected2) {
+ return;
+ }
+ failed("Arrays.parallelSort(byte[],fromIndex,toIndex) shouldn't " +
+ "catch null array");
+ }
+ failed("Arrays.parallelSort(byte[]) shouldn't catch null array");
+ }
+
+ private static void testEmptyAndNullFloatArray() {
+ ourDescription = "Check empty and null array";
+ Arrays.parallelSort(new float[]{});
+ Arrays.parallelSort(new float[]{}, 0, 0);
+
+ try {
+ Arrays.parallelSort((float[]) null);
+ } catch (NullPointerException expected) {
+ try {
+ Arrays.parallelSort((float[]) null, 0, 0);
+ } catch (NullPointerException expected2) {
+ return;
+ }
+ failed("Arrays.parallelSort(float[],fromIndex,toIndex) shouldn't " +
+ "catch null array");
+ }
+ failed("Arrays.parallelSort(float[]) shouldn't catch null array");
+ }
+
+ private static void testEmptyAndNullDoubleArray() {
+ ourDescription = "Check empty and null array";
+ Arrays.parallelSort(new double[]{});
+ Arrays.parallelSort(new double[]{}, 0, 0);
+
+ try {
+ Arrays.parallelSort((double[]) null);
+ } catch (NullPointerException expected) {
+ try {
+ Arrays.parallelSort((double[]) null, 0, 0);
+ } catch (NullPointerException expected2) {
+ return;
+ }
+ failed("Arrays.parallelSort(double[],fromIndex,toIndex) shouldn't " +
+ "catch null array");
+ }
+ failed("Arrays.parallelSort(double[]) shouldn't catch null array");
+ }
+
+ private static void testAndCheckSubArray(int length) {
+ ourDescription = "Check sorting of subarray";
+ int[] golden = new int[length];
+ boolean newLine = false;
+
+ for (int m = 1; m < length / 2; m *= 2) {
+ newLine = true;
+ int fromIndex = m;
+ int toIndex = length - m;
+
+ prepareSubArray(golden, fromIndex, toIndex, m);
+ int[] test = golden.clone();
+
+ for (TypeConverter converter : TypeConverter.values()) {
+ out.println("Test 'subarray': " + converter +
+ " length = " + length + ", m = " + m);
+ Object convertedGolden = converter.convert(golden);
+ Object convertedTest = converter.convert(test);
+ sortSubArray(convertedTest, fromIndex, toIndex);
+ checkSubArray(convertedTest, fromIndex, toIndex, m);
+ }
+ }
+ if (newLine) {
+ out.println();
+ }
+ }
+
+ private static void testAndCheckRange(int length) {
+ ourDescription = "Check range check";
+ int[] golden = new int[length];
+
+ for (int m = 1; m < 2 * length; m *= 2) {
+ for (int i = 1; i <= length; i++) {
+ golden[i - 1] = i % m + m % i;
+ }
+ for (TypeConverter converter : TypeConverter.values()) {
+ out.println("Test 'range': " + converter +
+ ", length = " + length + ", m = " + m);
+ Object convertedGolden = converter.convert(golden);
+ checkRange(convertedGolden, m);
+ }
+ }
+ out.println();
+ }
+
+ private static void testStable(int length, MyRandom random) {
+ ourDescription = "Check if sorting is stable";
+ Pair[] a = build(length, random);
+
+ out.println("Test 'stable': " + "random = " + random.getSeed() +
+ ", length = " + length);
+ Arrays.parallelSort(a);
+ checkSorted(a);
+ checkStable(a);
+ out.println();
+
+ a = build(length, random);
+
+ out.println("Test 'stable' comparator: " + "random = " + random.getSeed() +
+ ", length = " + length);
+ Arrays.parallelSort(a, pairCmp);
+ checkSorted(a);
+ checkStable(a);
+ out.println();
+
+ }
+
+ private static void checkSorted(Pair[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i].getKey() > a[i + 1].getKey()) {
+ failedSort(i, "" + a[i].getKey(), "" + a[i + 1].getKey());
+ }
+ }
+ }
+
+ private static void checkStable(Pair[] a) {
+ for (int i = 0; i < a.length / 4; ) {
+ int key1 = a[i].getKey();
+ int value1 = a[i++].getValue();
+ int key2 = a[i].getKey();
+ int value2 = a[i++].getValue();
+ int key3 = a[i].getKey();
+ int value3 = a[i++].getValue();
+ int key4 = a[i].getKey();
+ int value4 = a[i++].getValue();
+
+ if (!(key1 == key2 && key2 == key3 && key3 == key4)) {
+ failed("On position " + i + " keys are different " +
+ key1 + ", " + key2 + ", " + key3 + ", " + key4);
+ }
+ if (!(value1 < value2 && value2 < value3 && value3 < value4)) {
+ failed("Sorting is not stable at position " + i +
+ ". Second values have been changed: " + value1 + ", " +
+ value2 + ", " + value3 + ", " + value4);
+ }
+ }
+ }
+
+ private static Pair[] build(int length, Random random) {
+ Pair[] a = new Pair[length * 4];
+
+ for (int i = 0; i < a.length; ) {
+ int key = random.nextInt();
+ a[i++] = new Pair(key, 1);
+ a[i++] = new Pair(key, 2);
+ a[i++] = new Pair(key, 3);
+ a[i++] = new Pair(key, 4);
+ }
+ return a;
+ }
+
+ private static Comparator<Pair> pairCmp = new Comparator<Pair>() {
+ public int compare(Pair p1, Pair p2) {
+ return p1.compareTo(p2);
+ }
+ };
+
+ private static final class Pair implements Comparable<Pair> {
+ Pair(int key, int value) {
+ myKey = key;
+ myValue = value;
+ }
+
+ int getKey() {
+ return myKey;
+ }
+
+ int getValue() {
+ return myValue;
+ }
+
+ public int compareTo(Pair pair) {
+ if (myKey < pair.myKey) {
+ return -1;
+ }
+ if (myKey > pair.myKey) {
+ return 1;
+ }
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "(" + myKey + ", " + myValue + ")";
+ }
+
+ private int myKey;
+ private int myValue;
+ }
+
+
+ private static void testAndCheckWithInsertionSort(int length, MyRandom random) {
+ if (length > 1000) {
+ return;
+ }
+ ourDescription = "Check sorting with insertion sort";
+ int[] golden = new int[length];
+
+ for (int m = 1; m < 2 * length; m *= 2) {
+ for (UnsortedBuilder builder : UnsortedBuilder.values()) {
+ builder.build(golden, m, random);
+ int[] test = golden.clone();
+
+ for (TypeConverter converter : TypeConverter.values()) {
+ out.println("Test 'insertion sort': " + converter +
+ " " + builder + "random = " + random.getSeed() +
+ ", length = " + length + ", m = " + m);
+ Object convertedGolden = converter.convert(golden);
+ Object convertedTest1 = converter.convert(test);
+ Object convertedTest2 = converter.convert(test);
+ sort(convertedTest1);
+ sortByInsertionSort(convertedTest2);
+ compare(convertedTest1, convertedTest2);
+ }
+ }
+ }
+ out.println();
+ }
+
+ private static void testMergeSort(int length) {
+ if (length < 1000) {
+ return;
+ }
+ ourDescription = "Check merge sorting";
+ int[] golden = new int[length];
+ int period = 67; // java.util.DualPivotQuicksort.MAX_RUN_COUNT
+
+ for (int m = period - 2; m <= period + 2; m++) {
+ for (MergeBuilder builder : MergeBuilder.values()) {
+ builder.build(golden, m);
+ int[] test = golden.clone();
+
+ for (TypeConverter converter : TypeConverter.values()) {
+ out.println("Test 'merge sort': " + converter + " " +
+ builder + "length = " + length + ", m = " + m);
+ Object convertedGolden = converter.convert(golden);
+ sort(convertedGolden);
+ checkSorted(convertedGolden);
+ }
+ }
+ }
+ out.println();
+ }
+
+ private static void testAndCheckWithCheckSum(int length, MyRandom random) {
+ ourDescription = "Check sorting with check sum";
+ int[] golden = new int[length];
+
+ for (int m = 1; m < 2 * length; m *= 2) {
+ for (UnsortedBuilder builder : UnsortedBuilder.values()) {
+ builder.build(golden, m, random);
+ int[] test = golden.clone();
+
+ for (TypeConverter converter : TypeConverter.values()) {
+ out.println("Test 'check sum': " + converter +
+ " " + builder + "random = " + random.getSeed() +
+ ", length = " + length + ", m = " + m);
+ Object convertedGolden = converter.convert(golden);
+ Object convertedTest = converter.convert(test);
+ sort(convertedTest);
+ checkWithCheckSum(convertedTest, convertedGolden);
+ }
+ }
+ }
+ out.println();
+ }
+
+ private static void testAndCheckWithScrambling(int length, MyRandom random) {
+ ourDescription = "Check sorting with scrambling";
+ int[] golden = new int[length];
+
+ for (int m = 1; m <= 7; m++) {
+ if (m > length) {
+ break;
+ }
+ for (SortedBuilder builder : SortedBuilder.values()) {
+ builder.build(golden, m);
+ int[] test = golden.clone();
+ scramble(test, random);
+
+ for (TypeConverter converter : TypeConverter.values()) {
+ out.println("Test 'scrambling': " + converter +
+ " " + builder + "random = " + random.getSeed() +
+ ", length = " + length + ", m = " + m);
+ Object convertedGolden = converter.convert(golden);
+ Object convertedTest = converter.convert(test);
+ sort(convertedTest);
+ compare(convertedTest, convertedGolden);
+ }
+ }
+ }
+ out.println();
+ }
+
+ private static void testAndCheckFloat(int length, MyRandom random) {
+ ourDescription = "Check float sorting";
+ float[] golden = new float[length];
+ final int MAX = 10;
+ boolean newLine = false;
+
+ for (int a = 0; a <= MAX; a++) {
+ for (int g = 0; g <= MAX; g++) {
+ for (int z = 0; z <= MAX; z++) {
+ for (int n = 0; n <= MAX; n++) {
+ for (int p = 0; p <= MAX; p++) {
+ if (a + g + z + n + p > length) {
+ continue;
+ }
+ if (a + g + z + n + p < length) {
+ continue;
+ }
+ for (FloatBuilder builder : FloatBuilder.values()) {
+ out.println("Test 'float': random = " + random.getSeed() +
+ ", length = " + length + ", a = " + a + ", g = " +
+ g + ", z = " + z + ", n = " + n + ", p = " + p);
+ builder.build(golden, a, g, z, n, p, random);
+ float[] test = golden.clone();
+ scramble(test, random);
+ sort(test);
+ compare(test, golden, a, n, g);
+ }
+ newLine = true;
+ }
+ }
+ }
+ }
+ }
+ if (newLine) {
+ out.println();
+ }
+ }
+
+ private static void testAndCheckDouble(int length, MyRandom random) {
+ ourDescription = "Check double sorting";
+ double[] golden = new double[length];
+ final int MAX = 10;
+ boolean newLine = false;
+
+ for (int a = 0; a <= MAX; a++) {
+ for (int g = 0; g <= MAX; g++) {
+ for (int z = 0; z <= MAX; z++) {
+ for (int n = 0; n <= MAX; n++) {
+ for (int p = 0; p <= MAX; p++) {
+ if (a + g + z + n + p > length) {
+ continue;
+ }
+ if (a + g + z + n + p < length) {
+ continue;
+ }
+ for (DoubleBuilder builder : DoubleBuilder.values()) {
+ out.println("Test 'double': random = " + random.getSeed() +
+ ", length = " + length + ", a = " + a + ", g = " +
+ g + ", z = " + z + ", n = " + n + ", p = " + p);
+ builder.build(golden, a, g, z, n, p, random);
+ double[] test = golden.clone();
+ scramble(test, random);
+ sort(test);
+ compare(test, golden, a, n, g);
+ }
+ newLine = true;
+ }
+ }
+ }
+ }
+ }
+ if (newLine) {
+ out.println();
+ }
+ }
+
+ private static void prepareSubArray(int[] a, int fromIndex, int toIndex, int m) {
+ for (int i = 0; i < fromIndex; i++) {
+ a[i] = 0xDEDA;
+ }
+ int middle = (fromIndex + toIndex) >>> 1;
+ int k = 0;
+
+ for (int i = fromIndex; i < middle; i++) {
+ a[i] = k++;
+ }
+ for (int i = middle; i < toIndex; i++) {
+ a[i] = k--;
+ }
+ for (int i = toIndex; i < a.length; i++) {
+ a[i] = 0xBABA;
+ }
+ }
+
+ private static void scramble(int[] a, Random random) {
+ for (int i = 0; i < a.length * 7; i++) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
+ }
+
+ private static void scramble(float[] a, Random random) {
+ for (int i = 0; i < a.length * 7; i++) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
+ }
+
+ private static void scramble(double[] a, Random random) {
+ for (int i = 0; i < a.length * 7; i++) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
+ }
+
+ private static void swap(int[] a, int i, int j) {
+ int t = a[i];
+ a[i] = a[j];
+ a[j] = t;
+ }
+
+ private static void swap(float[] a, int i, int j) {
+ float t = a[i];
+ a[i] = a[j];
+ a[j] = t;
+ }
+
+ private static void swap(double[] a, int i, int j) {
+ double t = a[i];
+ a[i] = a[j];
+ a[j] = t;
+ }
+
+ private static enum TypeConverter {
+ INT {
+ Object convert(int[] a) {
+ return a.clone();
+ }
+ },
+ LONG {
+ Object convert(int[] a) {
+ long[] b = new long[a.length];
+
+ for (int i = 0; i < a.length; i++) {
+ b[i] = (long) a[i];
+ }
+ return b;
+ }
+ },
+ BYTE {
+ Object convert(int[] a) {
+ byte[] b = new byte[a.length];
+
+ for (int i = 0; i < a.length; i++) {
+ b[i] = (byte) a[i];
+ }
+ return b;
+ }
+ },
+ SHORT {
+ Object convert(int[] a) {
+ short[] b = new short[a.length];
+
+ for (int i = 0; i < a.length; i++) {
+ b[i] = (short) a[i];
+ }
+ return b;
+ }
+ },
+ CHAR {
+ Object convert(int[] a) {
+ char[] b = new char[a.length];
+
+ for (int i = 0; i < a.length; i++) {
+ b[i] = (char) a[i];
+ }
+ return b;
+ }
+ },
+ FLOAT {
+ Object convert(int[] a) {
+ float[] b = new float[a.length];
+
+ for (int i = 0; i < a.length; i++) {
+ b[i] = (float) a[i];
+ }
+ return b;
+ }
+ },
+ DOUBLE {
+ Object convert(int[] a) {
+ double[] b = new double[a.length];
+
+ for (int i = 0; i < a.length; i++) {
+ b[i] = (double) a[i];
+ }
+ return b;
+ }
+ },
+ INTEGER {
+ Object convert(int[] a) {
+ Integer[] b = new Integer[a.length];
+
+ for (int i = 0; i < a.length; i++) {
+ b[i] = new Integer(a[i]);
+ }
+ return b;
+ }
+ };
+
+ abstract Object convert(int[] a);
+
+ @Override public String toString() {
+ String name = name();
+
+ for (int i = name.length(); i < 9; i++) {
+ name += " ";
+ }
+ return name;
+ }
+ }
+
+ private static enum FloatBuilder {
+ SIMPLE {
+ void build(float[] x, int a, int g, int z, int n, int p, Random random) {
+ int fromIndex = 0;
+ float negativeValue = -random.nextFloat();
+ float positiveValue = random.nextFloat();
+
+ writeValue(x, negativeValue, fromIndex, n);
+ fromIndex += n;
+
+ writeValue(x, -0.0f, fromIndex, g);
+ fromIndex += g;
+
+ writeValue(x, 0.0f, fromIndex, z);
+ fromIndex += z;
+
+ writeValue(x, positiveValue, fromIndex, p);
+ fromIndex += p;
+
+ writeValue(x, Float.NaN, fromIndex, a);
+ }
+ };
+
+ abstract void build(float[] x, int a, int g, int z, int n, int p, Random random);
+ }
+
+ private static enum DoubleBuilder {
+ SIMPLE {
+ void build(double[] x, int a, int g, int z, int n, int p, Random random) {
+ int fromIndex = 0;
+ double negativeValue = -random.nextFloat();
+ double positiveValue = random.nextFloat();
+
+ writeValue(x, negativeValue, fromIndex, n);
+ fromIndex += n;
+
+ writeValue(x, -0.0d, fromIndex, g);
+ fromIndex += g;
+
+ writeValue(x, 0.0d, fromIndex, z);
+ fromIndex += z;
+
+ writeValue(x, positiveValue, fromIndex, p);
+ fromIndex += p;
+
+ writeValue(x, Double.NaN, fromIndex, a);
+ }
+ };
+
+ abstract void build(double[] x, int a, int g, int z, int n, int p, Random random);
+ }
+
+ private static void writeValue(float[] a, float value, int fromIndex, int count) {
+ for (int i = fromIndex; i < fromIndex + count; i++) {
+ a[i] = value;
+ }
+ }
+
+ private static void compare(float[] a, float[] b, int numNaN, int numNeg, int numNegZero) {
+ for (int i = a.length - numNaN; i < a.length; i++) {
+ if (a[i] == a[i]) {
+ failed("On position " + i + " must be NaN instead of " + a[i]);
+ }
+ }
+ final int NEGATIVE_ZERO = Float.floatToIntBits(-0.0f);
+
+ for (int i = numNeg; i < numNeg + numNegZero; i++) {
+ if (NEGATIVE_ZERO != Float.floatToIntBits(a[i])) {
+ failed("On position " + i + " must be -0.0 instead of " + a[i]);
+ }
+ }
+ for (int i = 0; i < a.length - numNaN; i++) {
+ if (a[i] != b[i]) {
+ failedCompare(i, "" + a[i], "" + b[i]);
+ }
+ }
+ }
+
+ private static void writeValue(double[] a, double value, int fromIndex, int count) {
+ for (int i = fromIndex; i < fromIndex + count; i++) {
+ a[i] = value;
+ }
+ }
+
+ private static void compare(double[] a, double[] b, int numNaN, int numNeg, int numNegZero) {
+ for (int i = a.length - numNaN; i < a.length; i++) {
+ if (a[i] == a[i]) {
+ failed("On position " + i + " must be NaN instead of " + a[i]);
+ }
+ }
+ final long NEGATIVE_ZERO = Double.doubleToLongBits(-0.0d);
+
+ for (int i = numNeg; i < numNeg + numNegZero; i++) {
+ if (NEGATIVE_ZERO != Double.doubleToLongBits(a[i])) {
+ failed("On position " + i + " must be -0.0 instead of " + a[i]);
+ }
+ }
+ for (int i = 0; i < a.length - numNaN; i++) {
+ if (a[i] != b[i]) {
+ failedCompare(i, "" + a[i], "" + b[i]);
+ }
+ }
+ }
+
+ private static enum SortedBuilder {
+ REPEATED {
+ void build(int[] a, int m) {
+ int period = a.length / m;
+ int i = 0;
+ int k = 0;
+
+ while (true) {
+ for (int t = 1; t <= period; t++) {
+ if (i >= a.length) {
+ return;
+ }
+ a[i++] = k;
+ }
+ if (i >= a.length) {
+ return;
+ }
+ k++;
+ }
+ }
+ },
+ ORGAN_PIPES {
+ void build(int[] a, int m) {
+ int i = 0;
+ int k = m;
+
+ while (true) {
+ for (int t = 1; t <= m; t++) {
+ if (i >= a.length) {
+ return;
+ }
+ a[i++] = k;
+ }
+ }
+ }
+ };
+
+ abstract void build(int[] a, int m);
+
+ @Override public String toString() {
+ String name = name();
+
+ for (int i = name.length(); i < 12; i++) {
+ name += " ";
+ }
+ return name;
+ }
+ }
+
+ private static enum MergeBuilder {
+ ASCENDING {
+ void build(int[] a, int m) {
+ int period = a.length / m;
+ int v = 1, i = 0;
+
+ for (int k = 0; k < m; k++) {
+ v = 1;
+ for (int p = 0; p < period; p++) {
+ a[i++] = v++;
+ }
+ }
+ for (int j = i; j < a.length - 1; j++) {
+ a[j] = v++;
+ }
+ a[a.length - 1] = 0;
+ }
+ },
+ DESCENDING {
+ void build(int[] a, int m) {
+ int period = a.length / m;
+ int v = -1, i = 0;
+
+ for (int k = 0; k < m; k++) {
+ v = -1;
+ for (int p = 0; p < period; p++) {
+ a[i++] = v--;
+ }
+ }
+ for (int j = i; j < a.length - 1; j++) {
+ a[j] = v--;
+ }
+ a[a.length - 1] = 0;
+ }
+ };
+
+ abstract void build(int[] a, int m);
+
+ @Override public String toString() {
+ String name = name();
+
+ for (int i = name.length(); i < 12; i++) {
+ name += " ";
+ }
+ return name;
+ }
+ }
+
+ private static enum UnsortedBuilder {
+ RANDOM {
+ void build(int[] a, int m, Random random) {
+ for (int i = 0; i < a.length; i++) {
+ a[i] = random.nextInt();
+ }
+ }
+ },
+ ASCENDING {
+ void build(int[] a, int m, Random random) {
+ for (int i = 0; i < a.length; i++) {
+ a[i] = m + i;
+ }
+ }
+ },
+ DESCENDING {
+ void build(int[] a, int m, Random random) {
+ for (int i = 0; i < a.length; i++) {
+ a[i] = a.length - m - i;
+ }
+ }
+ },
+ ALL_EQUAL {
+ void build(int[] a, int m, Random random) {
+ for (int i = 0; i < a.length; i++) {
+ a[i] = m;
+ }
+ }
+ },
+ SAW {
+ void build(int[] a, int m, Random random) {
+ int incCount = 1;
+ int decCount = a.length;
+ int i = 0;
+ int period = m--;
+
+ while (true) {
+ for (int k = 1; k <= period; k++) {
+ if (i >= a.length) {
+ return;
+ }
+ a[i++] = incCount++;
+ }
+ period += m;
+
+ for (int k = 1; k <= period; k++) {
+ if (i >= a.length) {
+ return;
+ }
+ a[i++] = decCount--;
+ }
+ period += m;
+ }
+ }
+ },
+ REPEATED {
+ void build(int[] a, int m, Random random) {
+ for (int i = 0; i < a.length; i++) {
+ a[i] = i % m;
+ }
+ }
+ },
+ DUPLICATED {
+ void build(int[] a, int m, Random random) {
+ for (int i = 0; i < a.length; i++) {
+ a[i] = random.nextInt(m);
+ }
+ }
+ },
+ ORGAN_PIPES {
+ void build(int[] a, int m, Random random) {
+ int middle = a.length / (m + 1);
+
+ for (int i = 0; i < middle; i++) {
+ a[i] = i;
+ }
+ for (int i = middle; i < a.length; i++) {
+ a[i] = a.length - i - 1;
+ }
+ }
+ },
+ STAGGER {
+ void build(int[] a, int m, Random random) {
+ for (int i = 0; i < a.length; i++) {
+ a[i] = (i * m + i) % a.length;
+ }
+ }
+ },
+ PLATEAU {
+ void build(int[] a, int m, Random random) {
+ for (int i = 0; i < a.length; i++) {
+ a[i] = Math.min(i, m);
+ }
+ }
+ },
+ SHUFFLE {
+ void build(int[] a, int m, Random random) {
+ int x = 0, y = 0;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = random.nextBoolean() ? (x += 2) : (y += 2);
+ }
+ }
+ };
+
+ abstract void build(int[] a, int m, Random random);
+
+ @Override public String toString() {
+ String name = name();
+
+ for (int i = name.length(); i < 12; i++) {
+ name += " ";
+ }
+ return name;
+ }
+ }
+
+ private static void checkWithCheckSum(Object test, Object golden) {
+ checkSorted(test);
+ checkCheckSum(test, golden);
+ }
+
+ private static void failed(String message) {
+ err.format("\n*** TEST FAILED - %s.\n\n%s.\n\n", ourDescription, message);
+ throw new RuntimeException("Test failed - see log file for details");
+ }
+
+ private static void failedSort(int index, String value1, String value2) {
+ failed("Array is not sorted at " + index + "-th position: " +
+ value1 + " and " + value2);
+ }
+
+ private static void failedCompare(int index, String value1, String value2) {
+ failed("On position " + index + " must be " + value2 + " instead of " + value1);
+ }
+
+ private static void compare(Object test, Object golden) {
+ if (test instanceof int[]) {
+ compare((int[]) test, (int[]) golden);
+ } else if (test instanceof long[]) {
+ compare((long[]) test, (long[]) golden);
+ } else if (test instanceof short[]) {
+ compare((short[]) test, (short[]) golden);
+ } else if (test instanceof byte[]) {
+ compare((byte[]) test, (byte[]) golden);
+ } else if (test instanceof char[]) {
+ compare((char[]) test, (char[]) golden);
+ } else if (test instanceof float[]) {
+ compare((float[]) test, (float[]) golden);
+ } else if (test instanceof double[]) {
+ compare((double[]) test, (double[]) golden);
+ } else if (test instanceof Integer[]) {
+ compare((Integer[]) test, (Integer[]) golden);
+ } else {
+ failed("Unknow type of array: " + test + " of class " +
+ test.getClass().getName());
+ }
+ }
+
+ private static void compare(int[] a, int[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ failedCompare(i, "" + a[i], "" + b[i]);
+ }
+ }
+ }
+
+ private static void compare(long[] a, long[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ failedCompare(i, "" + a[i], "" + b[i]);
+ }
+ }
+ }
+
+ private static void compare(short[] a, short[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ failedCompare(i, "" + a[i], "" + b[i]);
+ }
+ }
+ }
+
+ private static void compare(byte[] a, byte[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ failedCompare(i, "" + a[i], "" + b[i]);
+ }
+ }
+ }
+
+ private static void compare(char[] a, char[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ failedCompare(i, "" + a[i], "" + b[i]);
+ }
+ }
+ }
+
+ private static void compare(float[] a, float[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ failedCompare(i, "" + a[i], "" + b[i]);
+ }
+ }
+ }
+
+ private static void compare(double[] a, double[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i]) {
+ failedCompare(i, "" + a[i], "" + b[i]);
+ }
+ }
+ }
+
+ private static void compare(Integer[] a, Integer[] b) {
+ for (int i = 0; i < a.length; i++) {
+ if (a[i].compareTo(b[i]) != 0) {
+ failedCompare(i, "" + a[i], "" + b[i]);
+ }
+ }
+ }
+
+ private static void checkSorted(Object object) {
+ if (object instanceof int[]) {
+ checkSorted((int[]) object);
+ } else if (object instanceof long[]) {
+ checkSorted((long[]) object);
+ } else if (object instanceof short[]) {
+ checkSorted((short[]) object);
+ } else if (object instanceof byte[]) {
+ checkSorted((byte[]) object);
+ } else if (object instanceof char[]) {
+ checkSorted((char[]) object);
+ } else if (object instanceof float[]) {
+ checkSorted((float[]) object);
+ } else if (object instanceof double[]) {
+ checkSorted((double[]) object);
+ } else if (object instanceof Integer[]) {
+ checkSorted((Integer[]) object);
+ } else {
+ failed("Unknow type of array: " + object + " of class " +
+ object.getClass().getName());
+ }
+ }
+
+ private static void checkSorted(int[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+ }
+
+ private static void checkSorted(long[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+ }
+
+ private static void checkSorted(short[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+ }
+
+ private static void checkSorted(byte[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+ }
+
+ private static void checkSorted(char[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+ }
+
+ private static void checkSorted(float[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+ }
+
+ private static void checkSorted(double[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+ }
+
+ private static void checkSorted(Integer[] a) {
+ for (int i = 0; i < a.length - 1; i++) {
+ if (a[i].intValue() > a[i + 1].intValue()) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+ }
+
+ private static void checkCheckSum(Object test, Object golden) {
+ if (checkSumXor(test) != checkSumXor(golden)) {
+ failed("Original and sorted arrays are not identical [xor]");
+ }
+ if (checkSumPlus(test) != checkSumPlus(golden)) {
+ failed("Original and sorted arrays are not identical [plus]");
+ }
+ }
+
+ private static int checkSumXor(Object object) {
+ if (object instanceof int[]) {
+ return checkSumXor((int[]) object);
+ } else if (object instanceof long[]) {
+ return checkSumXor((long[]) object);
+ } else if (object instanceof short[]) {
+ return checkSumXor((short[]) object);
+ } else if (object instanceof byte[]) {
+ return checkSumXor((byte[]) object);
+ } else if (object instanceof char[]) {
+ return checkSumXor((char[]) object);
+ } else if (object instanceof float[]) {
+ return checkSumXor((float[]) object);
+ } else if (object instanceof double[]) {
+ return checkSumXor((double[]) object);
+ } else if (object instanceof Integer[]) {
+ return checkSumXor((Integer[]) object);
+ } else {
+ failed("Unknow type of array: " + object + " of class " +
+ object.getClass().getName());
+ return -1;
+ }
+ }
+
+ private static int checkSumXor(Integer[] a) {
+ int checkSum = 0;
+
+ for (Integer e : a) {
+ checkSum ^= e.intValue();
+ }
+ return checkSum;
+ }
+
+ private static int checkSumXor(int[] a) {
+ int checkSum = 0;
+
+ for (int e : a) {
+ checkSum ^= e;
+ }
+ return checkSum;
+ }
+
+ private static int checkSumXor(long[] a) {
+ long checkSum = 0;
+
+ for (long e : a) {
+ checkSum ^= e;
+ }
+ return (int) checkSum;
+ }
+
+ private static int checkSumXor(short[] a) {
+ short checkSum = 0;
+
+ for (short e : a) {
+ checkSum ^= e;
+ }
+ return (int) checkSum;
+ }
+
+ private static int checkSumXor(byte[] a) {
+ byte checkSum = 0;
+
+ for (byte e : a) {
+ checkSum ^= e;
+ }
+ return (int) checkSum;
+ }
+
+ private static int checkSumXor(char[] a) {
+ char checkSum = 0;
+
+ for (char e : a) {
+ checkSum ^= e;
+ }
+ return (int) checkSum;
+ }
+
+ private static int checkSumXor(float[] a) {
+ int checkSum = 0;
+
+ for (float e : a) {
+ checkSum ^= (int) e;
+ }
+ return checkSum;
+ }
+
+ private static int checkSumXor(double[] a) {
+ int checkSum = 0;
+
+ for (double e : a) {
+ checkSum ^= (int) e;
+ }
+ return checkSum;
+ }
+
+ private static int checkSumPlus(Object object) {
+ if (object instanceof int[]) {
+ return checkSumPlus((int[]) object);
+ } else if (object instanceof long[]) {
+ return checkSumPlus((long[]) object);
+ } else if (object instanceof short[]) {
+ return checkSumPlus((short[]) object);
+ } else if (object instanceof byte[]) {
+ return checkSumPlus((byte[]) object);
+ } else if (object instanceof char[]) {
+ return checkSumPlus((char[]) object);
+ } else if (object instanceof float[]) {
+ return checkSumPlus((float[]) object);
+ } else if (object instanceof double[]) {
+ return checkSumPlus((double[]) object);
+ } else if (object instanceof Integer[]) {
+ return checkSumPlus((Integer[]) object);
+ } else {
+ failed("Unknow type of array: " + object + " of class " +
+ object.getClass().getName());
+ return -1;
+ }
+ }
+
+ private static int checkSumPlus(int[] a) {
+ int checkSum = 0;
+
+ for (int e : a) {
+ checkSum += e;
+ }
+ return checkSum;
+ }
+
+ private static int checkSumPlus(long[] a) {
+ long checkSum = 0;
+
+ for (long e : a) {
+ checkSum += e;
+ }
+ return (int) checkSum;
+ }
+
+ private static int checkSumPlus(short[] a) {
+ short checkSum = 0;
+
+ for (short e : a) {
+ checkSum += e;
+ }
+ return (int) checkSum;
+ }
+
+ private static int checkSumPlus(byte[] a) {
+ byte checkSum = 0;
+
+ for (byte e : a) {
+ checkSum += e;
+ }
+ return (int) checkSum;
+ }
+
+ private static int checkSumPlus(char[] a) {
+ char checkSum = 0;
+
+ for (char e : a) {
+ checkSum += e;
+ }
+ return (int) checkSum;
+ }
+
+ private static int checkSumPlus(float[] a) {
+ int checkSum = 0;
+
+ for (float e : a) {
+ checkSum += (int) e;
+ }
+ return checkSum;
+ }
+
+ private static int checkSumPlus(double[] a) {
+ int checkSum = 0;
+
+ for (double e : a) {
+ checkSum += (int) e;
+ }
+ return checkSum;
+ }
+
+ private static int checkSumPlus(Integer[] a) {
+ int checkSum = 0;
+
+ for (Integer e : a) {
+ checkSum += e.intValue();
+ }
+ return checkSum;
+ }
+
+ private static void sortByInsertionSort(Object object) {
+ if (object instanceof int[]) {
+ sortByInsertionSort((int[]) object);
+ } else if (object instanceof long[]) {
+ sortByInsertionSort((long[]) object);
+ } else if (object instanceof short[]) {
+ sortByInsertionSort((short[]) object);
+ } else if (object instanceof byte[]) {
+ sortByInsertionSort((byte[]) object);
+ } else if (object instanceof char[]) {
+ sortByInsertionSort((char[]) object);
+ } else if (object instanceof float[]) {
+ sortByInsertionSort((float[]) object);
+ } else if (object instanceof double[]) {
+ sortByInsertionSort((double[]) object);
+ } else if (object instanceof Integer[]) {
+ sortByInsertionSort((Integer[]) object);
+ } else {
+ failed("Unknow type of array: " + object + " of class " +
+ object.getClass().getName());
+ }
+ }
+
+ private static void sortByInsertionSort(int[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ int ai = a[i];
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private static void sortByInsertionSort(long[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ long ai = a[i];
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private static void sortByInsertionSort(short[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ short ai = a[i];
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private static void sortByInsertionSort(byte[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ byte ai = a[i];
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private static void sortByInsertionSort(char[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ char ai = a[i];
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private static void sortByInsertionSort(float[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ float ai = a[i];
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private static void sortByInsertionSort(double[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ double ai = a[i];
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private static void sortByInsertionSort(Integer[] a) {
+ for (int j, i = 1; i < a.length; i++) {
+ Integer ai = a[i];
+ for (j = i - 1; j >= 0 && ai < a[j]; j--) {
+ a[j + 1] = a[j];
+ }
+ a[j + 1] = ai;
+ }
+ }
+
+ private static void sort(Object object) {
+ if (object instanceof int[]) {
+ Arrays.parallelSort((int[]) object);
+ } else if (object instanceof long[]) {
+ Arrays.parallelSort((long[]) object);
+ } else if (object instanceof short[]) {
+ Arrays.parallelSort((short[]) object);
+ } else if (object instanceof byte[]) {
+ Arrays.parallelSort((byte[]) object);
+ } else if (object instanceof char[]) {
+ Arrays.parallelSort((char[]) object);
+ } else if (object instanceof float[]) {
+ Arrays.parallelSort((float[]) object);
+ } else if (object instanceof double[]) {
+ Arrays.parallelSort((double[]) object);
+ } else if (object instanceof Integer[]) {
+ Arrays.parallelSort((Integer[]) object);
+ } else {
+ failed("Unknow type of array: " + object + " of class " +
+ object.getClass().getName());
+ }
+ }
+
+ private static void sortSubArray(Object object, int fromIndex, int toIndex) {
+ if (object instanceof int[]) {
+ Arrays.parallelSort((int[]) object, fromIndex, toIndex);
+ } else if (object instanceof long[]) {
+ Arrays.parallelSort((long[]) object, fromIndex, toIndex);
+ } else if (object instanceof short[]) {
+ Arrays.parallelSort((short[]) object, fromIndex, toIndex);
+ } else if (object instanceof byte[]) {
+ Arrays.parallelSort((byte[]) object, fromIndex, toIndex);
+ } else if (object instanceof char[]) {
+ Arrays.parallelSort((char[]) object, fromIndex, toIndex);
+ } else if (object instanceof float[]) {
+ Arrays.parallelSort((float[]) object, fromIndex, toIndex);
+ } else if (object instanceof double[]) {
+ Arrays.parallelSort((double[]) object, fromIndex, toIndex);
+ } else if (object instanceof Integer[]) {
+ Arrays.parallelSort((Integer[]) object, fromIndex, toIndex);
+ } else {
+ failed("Unknow type of array: " + object + " of class " +
+ object.getClass().getName());
+ }
+ }
+
+ private static void checkSubArray(Object object, int fromIndex, int toIndex, int m) {
+ if (object instanceof int[]) {
+ checkSubArray((int[]) object, fromIndex, toIndex, m);
+ } else if (object instanceof long[]) {
+ checkSubArray((long[]) object, fromIndex, toIndex, m);
+ } else if (object instanceof short[]) {
+ checkSubArray((short[]) object, fromIndex, toIndex, m);
+ } else if (object instanceof byte[]) {
+ checkSubArray((byte[]) object, fromIndex, toIndex, m);
+ } else if (object instanceof char[]) {
+ checkSubArray((char[]) object, fromIndex, toIndex, m);
+ } else if (object instanceof float[]) {
+ checkSubArray((float[]) object, fromIndex, toIndex, m);
+ } else if (object instanceof double[]) {
+ checkSubArray((double[]) object, fromIndex, toIndex, m);
+ } else if (object instanceof Integer[]) {
+ checkSubArray((Integer[]) object, fromIndex, toIndex, m);
+ } else {
+ failed("Unknow type of array: " + object + " of class " +
+ object.getClass().getName());
+ }
+ }
+
+ private static void checkSubArray(Integer[] a, int fromIndex, int toIndex, int m) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i].intValue() != 0xDEDA) {
+ failed("Range sort changes left element on position " + i +
+ ": " + a[i] + ", must be " + 0xDEDA);
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i].intValue() > a[i + 1].intValue()) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i].intValue() != 0xBABA) {
+ failed("Range sort changes right element on position " + i +
+ ": " + a[i] + ", must be " + 0xBABA);
+ }
+ }
+ }
+
+ private static void checkSubArray(int[] a, int fromIndex, int toIndex, int m) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != 0xDEDA) {
+ failed("Range sort changes left element on position " + i +
+ ": " + a[i] + ", must be " + 0xDEDA);
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != 0xBABA) {
+ failed("Range sort changes right element on position " + i +
+ ": " + a[i] + ", must be " + 0xBABA);
+ }
+ }
+ }
+
+ private static void checkSubArray(byte[] a, int fromIndex, int toIndex, int m) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (byte) 0xDEDA) {
+ failed("Range sort changes left element on position " + i +
+ ": " + a[i] + ", must be " + 0xDEDA);
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (byte) 0xBABA) {
+ failed("Range sort changes right element on position " + i +
+ ": " + a[i] + ", must be " + 0xBABA);
+ }
+ }
+ }
+
+ private static void checkSubArray(long[] a, int fromIndex, int toIndex, int m) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (long) 0xDEDA) {
+ failed("Range sort changes left element on position " + i +
+ ": " + a[i] + ", must be " + 0xDEDA);
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (long) 0xBABA) {
+ failed("Range sort changes right element on position " + i +
+ ": " + a[i] + ", must be " + 0xBABA);
+ }
+ }
+ }
+
+ private static void checkSubArray(char[] a, int fromIndex, int toIndex, int m) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (char) 0xDEDA) {
+ failed("Range sort changes left element on position " + i +
+ ": " + a[i] + ", must be " + 0xDEDA);
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (char) 0xBABA) {
+ failed("Range sort changes right element on position " + i +
+ ": " + a[i] + ", must be " + 0xBABA);
+ }
+ }
+ }
+
+ private static void checkSubArray(short[] a, int fromIndex, int toIndex, int m) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (short) 0xDEDA) {
+ failed("Range sort changes left element on position " + i +
+ ": " + a[i] + ", must be " + 0xDEDA);
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (short) 0xBABA) {
+ failed("Range sort changes right element on position " + i +
+ ": " + a[i] + ", must be " + 0xBABA);
+ }
+ }
+ }
+
+ private static void checkSubArray(float[] a, int fromIndex, int toIndex, int m) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (float) 0xDEDA) {
+ failed("Range sort changes left element on position " + i +
+ ": " + a[i] + ", must be " + 0xDEDA);
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (float) 0xBABA) {
+ failed("Range sort changes right element on position " + i +
+ ": " + a[i] + ", must be " + 0xBABA);
+ }
+ }
+ }
+
+ private static void checkSubArray(double[] a, int fromIndex, int toIndex, int m) {
+ for (int i = 0; i < fromIndex; i++) {
+ if (a[i] != (double) 0xDEDA) {
+ failed("Range sort changes left element on position " + i +
+ ": " + a[i] + ", must be " + 0xDEDA);
+ }
+ }
+
+ for (int i = fromIndex; i < toIndex - 1; i++) {
+ if (a[i] > a[i + 1]) {
+ failedSort(i, "" + a[i], "" + a[i + 1]);
+ }
+ }
+
+ for (int i = toIndex; i < a.length; i++) {
+ if (a[i] != (double) 0xBABA) {
+ failed("Range sort changes right element on position " + i +
+ ": " + a[i] + ", must be " + 0xBABA);
+ }
+ }
+ }
+
+ private static void checkRange(Object object, int m) {
+ if (object instanceof int[]) {
+ checkRange((int[]) object, m);
+ } else if (object instanceof long[]) {
+ checkRange((long[]) object, m);
+ } else if (object instanceof short[]) {
+ checkRange((short[]) object, m);
+ } else if (object instanceof byte[]) {
+ checkRange((byte[]) object, m);
+ } else if (object instanceof char[]) {
+ checkRange((char[]) object, m);
+ } else if (object instanceof float[]) {
+ checkRange((float[]) object, m);
+ } else if (object instanceof double[]) {
+ checkRange((double[]) object, m);
+ } else if (object instanceof Integer[]) {
+ checkRange((Integer[]) object, m);
+ } else {
+ failed("Unknow type of array: " + object + " of class " +
+ object.getClass().getName());
+ }
+ }
+
+ private static void checkRange(Integer[] a, int m) {
+ try {
+ Arrays.parallelSort(a, m + 1, m);
+
+ failed("ParallelSort does not throw IllegalArgumentException " +
+ " as expected: fromIndex = " + (m + 1) +
+ " toIndex = " + m);
+ }
+ catch (IllegalArgumentException iae) {
+ try {
+ Arrays.parallelSort(a, -m, a.length);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: fromIndex = " + (-m));
+ }
+ catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ Arrays.parallelSort(a, 0, a.length + m);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: toIndex = " + (a.length + m));
+ }
+ catch (ArrayIndexOutOfBoundsException aie) {
+ return;
+ }
+ }
+ }
+ }
+
+ private static void checkRange(int[] a, int m) {
+ try {
+ Arrays.parallelSort(a, m + 1, m);
+
+ failed("ParallelSort does not throw IllegalArgumentException " +
+ " as expected: fromIndex = " + (m + 1) +
+ " toIndex = " + m);
+ }
+ catch (IllegalArgumentException iae) {
+ try {
+ Arrays.parallelSort(a, -m, a.length);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: fromIndex = " + (-m));
+ }
+ catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ Arrays.parallelSort(a, 0, a.length + m);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: toIndex = " + (a.length + m));
+ }
+ catch (ArrayIndexOutOfBoundsException aie) {
+ return;
+ }
+ }
+ }
+ }
+
+ private static void checkRange(long[] a, int m) {
+ try {
+ Arrays.parallelSort(a, m + 1, m);
+
+ failed("ParallelSort does not throw IllegalArgumentException " +
+ " as expected: fromIndex = " + (m + 1) +
+ " toIndex = " + m);
+ }
+ catch (IllegalArgumentException iae) {
+ try {
+ Arrays.parallelSort(a, -m, a.length);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: fromIndex = " + (-m));
+ }
+ catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ Arrays.parallelSort(a, 0, a.length + m);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: toIndex = " + (a.length + m));
+ }
+ catch (ArrayIndexOutOfBoundsException aie) {
+ return;
+ }
+ }
+ }
+ }
+
+ private static void checkRange(byte[] a, int m) {
+ try {
+ Arrays.parallelSort(a, m + 1, m);
+
+ failed("ParallelSort does not throw IllegalArgumentException " +
+ " as expected: fromIndex = " + (m + 1) +
+ " toIndex = " + m);
+ }
+ catch (IllegalArgumentException iae) {
+ try {
+ Arrays.parallelSort(a, -m, a.length);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: fromIndex = " + (-m));
+ }
+ catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ Arrays.parallelSort(a, 0, a.length + m);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: toIndex = " + (a.length + m));
+ }
+ catch (ArrayIndexOutOfBoundsException aie) {
+ return;
+ }
+ }
+ }
+ }
+
+ private static void checkRange(short[] a, int m) {
+ try {
+ Arrays.parallelSort(a, m + 1, m);
+
+ failed("ParallelSort does not throw IllegalArgumentException " +
+ " as expected: fromIndex = " + (m + 1) +
+ " toIndex = " + m);
+ }
+ catch (IllegalArgumentException iae) {
+ try {
+ Arrays.parallelSort(a, -m, a.length);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: fromIndex = " + (-m));
+ }
+ catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ Arrays.parallelSort(a, 0, a.length + m);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: toIndex = " + (a.length + m));
+ }
+ catch (ArrayIndexOutOfBoundsException aie) {
+ return;
+ }
+ }
+ }
+ }
+
+ private static void checkRange(char[] a, int m) {
+ try {
+ Arrays.parallelSort(a, m + 1, m);
+
+ failed("ParallelSort does not throw IllegalArgumentException " +
+ " as expected: fromIndex = " + (m + 1) +
+ " toIndex = " + m);
+ }
+ catch (IllegalArgumentException iae) {
+ try {
+ Arrays.parallelSort(a, -m, a.length);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: fromIndex = " + (-m));
+ }
+ catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ Arrays.parallelSort(a, 0, a.length + m);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: toIndex = " + (a.length + m));
+ }
+ catch (ArrayIndexOutOfBoundsException aie) {
+ return;
+ }
+ }
+ }
+ }
+
+ private static void checkRange(float[] a, int m) {
+ try {
+ Arrays.parallelSort(a, m + 1, m);
+
+ failed("ParallelSort does not throw IllegalArgumentException " +
+ " as expected: fromIndex = " + (m + 1) +
+ " toIndex = " + m);
+ }
+ catch (IllegalArgumentException iae) {
+ try {
+ Arrays.parallelSort(a, -m, a.length);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: fromIndex = " + (-m));
+ }
+ catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ Arrays.parallelSort(a, 0, a.length + m);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: toIndex = " + (a.length + m));
+ }
+ catch (ArrayIndexOutOfBoundsException aie) {
+ return;
+ }
+ }
+ }
+ }
+
+ private static void checkRange(double[] a, int m) {
+ try {
+ Arrays.parallelSort(a, m + 1, m);
+
+ failed("ParallelSort does not throw IllegalArgumentException " +
+ " as expected: fromIndex = " + (m + 1) +
+ " toIndex = " + m);
+ }
+ catch (IllegalArgumentException iae) {
+ try {
+ Arrays.parallelSort(a, -m, a.length);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: fromIndex = " + (-m));
+ }
+ catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ Arrays.parallelSort(a, 0, a.length + m);
+
+ failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " +
+ " as expected: toIndex = " + (a.length + m));
+ }
+ catch (ArrayIndexOutOfBoundsException aie) {
+ return;
+ }
+ }
+ }
+ }
+
+ private static void outArray(Object[] a) {
+ for (int i = 0; i < a.length; i++) {
+ out.print(a[i] + " ");
+ }
+ out.println();
+ }
+
+ private static void outArray(int[] a) {
+ for (int i = 0; i < a.length; i++) {
+ out.print(a[i] + " ");
+ }
+ out.println();
+ }
+
+ private static void outArray(float[] a) {
+ for (int i = 0; i < a.length; i++) {
+ out.print(a[i] + " ");
+ }
+ out.println();
+ }
+
+ private static void outArray(double[] a) {
+ for (int i = 0; i < a.length; i++) {
+ out.print(a[i] + " ");
+ }
+ out.println();
+ }
+
+ private static class MyRandom extends Random {
+ MyRandom(long seed) {
+ super(seed);
+ mySeed = seed;
+ }
+
+ long getSeed() {
+ return mySeed;
+ }
+
+ private long mySeed;
+ }
+
+ private static String ourDescription;
+}
--- a/jdk/test/java/util/Collections/EmptyIterator.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/util/Collections/EmptyIterator.java Fri Dec 28 18:36:41 2012 -0800
@@ -23,12 +23,12 @@
/*
* @test
- * @bug 5017904 6356890
+ * @bug 5017904 6356890 8004928
* @summary Test empty iterators, enumerations, and collections
*/
+import static java.util.Collections.*;
import java.util.*;
-import static java.util.Collections.*;
public class EmptyIterator {
@@ -45,10 +45,13 @@
testEmptyIterator(emptyTable.values().iterator());
testEmptyIterator(emptyTable.entrySet().iterator());
- testEmptyEnumeration(javax.swing.tree.DefaultMutableTreeNode
- .EMPTY_ENUMERATION);
- testEmptyEnumeration(javax.swing.text.SimpleAttributeSet
- .EMPTY.getAttributeNames());
+ final Enumeration<EmptyIterator> finalEmptyTyped =
+ Collections.emptyEnumeration();
+ testEmptyEnumeration(finalEmptyTyped);
+
+ final Enumeration finalEmptyAbstract =
+ Collections.emptyEnumeration();
+ testEmptyEnumeration(finalEmptyAbstract);
@SuppressWarnings("unchecked") Iterator<?> x =
new sun.tools.java.MethodSet()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/Compatibility.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"
+ [<!ENTITY intEnt 'value3'> ]>
+<?PITarget PIContent?>
+<properties>
+<comment>Property With Other Encoding</comment>
+<entry key="Key1">value1</entry>
+<entry key="Key2"><![CDATA[<value2>]]></entry>
+<entry key="Key3">&intEnt;</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/CompatibilityTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/*
+ * @test
+ * @bug 8005280 8004371
+ * @summary Compatibility test
+ * @run main CompatibilityTest
+ * @run main/othervm -Dsun.util.spi.XmlPropertiesProvider=jdk.internal.util.xml.BasicXmlPropertiesProvider CompatibilityTest
+ */
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ * This is a behavior compatibility test.
+ * Although not defined by the properties.dtd, the constructs
+ * in Compatibility.xml are supported by the regular JDK XML
+ * Provider.
+ *
+ * @author: Joe Wang
+ */
+public class CompatibilityTest {
+
+ public static void main(String[] args) {
+ testInternalDTD();
+ }
+
+ /*
+ * Not in the spec, but the constructs work with the current JDK
+ */
+ static void testInternalDTD() {
+ String src = System.getProperty("test.src");
+ if (src == null) {
+ src = ".";
+ }
+ loadPropertyFile(src + "/Compatibility.xml");
+ }
+
+ /*
+ * 'Store' the populated 'Property' with the specified 'Encoding Type' as an
+ * XML file. Retrieve the same XML file and 'load' onto a new 'Property' object.
+ */
+ static void loadPropertyFile(String filename) {
+ try (InputStream in = new FileInputStream(filename)) {
+ Properties prop = new Properties();
+ prop.loadFromXML(in);
+ verifyProperites(prop);
+ } catch (IOException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ /*
+ * This method verifies the first key-value with the original string.
+ */
+ static void verifyProperites(Properties prop) {
+ try {
+ for (String key : prop.stringPropertyNames()) {
+ String val = prop.getProperty(key);
+ if (key.equals("Key1")) {
+ if (!val.equals("value1")) {
+ fail("Key:" + key + "'s value: \nExpected: value1\nFound: " + val);
+ }
+ } else if (key.equals("Key2")) {
+ if (!val.equals("<value2>")) {
+ fail("Key:" + key + "'s value: \nExpected: <value2>\nFound: " + val);
+ }
+ } else if (key.equals("Key3")) {
+ if (!val.equals("value3")) {
+ fail("Key:" + key + "'s value: \nExpected: value3\nFound: " + val);
+ }
+ }
+ }
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+
+ }
+
+ static void fail(String err) {
+ throw new RuntimeException(err);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/ConcurrentLoadAndStoreXML.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/* @test
+ * @bug 8005281
+ * @summary Test that the Properties storeToXML and loadFromXML methods are
+ * thread safe
+ * @run main ConcurrentLoadAndStoreXML
+ * @run main/othervm -Dsun.util.spi.XmlPropertiesProvider=jdk.internal.util.xml.BasicXmlPropertiesProvider ConcurrentLoadAndStoreXML
+
+ */
+
+import java.io.*;
+import java.util.Properties;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+public class ConcurrentLoadAndStoreXML {
+
+ static final Random RAND = new Random();
+
+ static volatile boolean done;
+
+ /**
+ * Simple task that bashes on storeToXML and loadFromXML until the "done"
+ * flag is set.
+ */
+ static class Basher implements Callable<Void> {
+ final Properties props;
+
+ Basher(Properties props) {
+ this.props = props;
+ }
+
+ public Void call() throws IOException {
+ while (!done) {
+
+ // store as XML format
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ props.storeToXML(out, null, "UTF-8");
+
+ // load from XML format
+ Properties p = new Properties();
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ p.loadFromXML(in);
+
+ // check that the properties are as expected
+ if (!p.equals(props))
+ throw new RuntimeException("Properties not equal");
+ }
+ return null;
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ final int NTASKS = 4 + RAND.nextInt(4);
+
+ // create Bashers with Properties of random keys and values
+ Basher[] basher = new Basher[NTASKS];
+ for (int i=0; i<NTASKS; i++) {
+ Properties props = new Properties();
+ for (int j=0; j<RAND.nextInt(100); j++) {
+ String key = "k" + RAND.nextInt(1000);
+ String value = "v" + RAND.nextInt(1000);
+ props.put(key, value);
+ }
+ basher[i] = new Basher(props);
+ }
+
+ ExecutorService pool = Executors.newFixedThreadPool(NTASKS);
+ try {
+ // kick off the bashers
+ Future<Void>[] task = new Future[NTASKS];
+ for (int i=0; i<NTASKS; i++) {
+ task[i] = pool.submit(basher[i]);
+ }
+
+ // give them time to interfere with each each
+ Thread.sleep(2000);
+ done = true;
+
+ // check the result
+ for (int i=0; i<NTASKS; i++) {
+ task[i].get();
+ }
+ } finally {
+ done = true;
+ pool.shutdown();
+ }
+
+ }
+}
--- a/jdk/test/java/util/Properties/LoadAndStoreXML.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/util/Properties/LoadAndStoreXML.java Fri Dec 28 18:36:41 2012 -0800
@@ -23,13 +23,16 @@
/*
* @test
- * @bug 8000354 8000685
+ * @bug 8000354 8000685 8004371
* @summary Basic test of storeToXML and loadToXML
+ * @run main LoadAndStoreXML
+ * @run main/othervm -Dsun.util.spi.XmlPropertiesProvider=jdk.internal.util.xml.BasicXmlPropertiesProvider LoadAndStoreXML
*/
import java.io.*;
import java.util.*;
import java.security.*;
+import java.nio.file.*;
public class LoadAndStoreXML {
@@ -67,9 +70,14 @@
* read with Properties#loadFromXML.
*/
static void testLoadAndStore(String encoding) throws IOException {
+ System.out.println("testLoadAndStore, encoding=" + encoding);
+
Properties props = new Properties();
props.put("k1", "foo");
props.put("k2", "bar");
+ props.put("k3", "\\u0020\\u0391\\u0392\\u0393\\u0394\\u0395\\u0396\\u0397");
+ props.put("k4", "\u7532\u9aa8\u6587");
+ props.put("k5", "<java.home>/lib/jaxp.properties");
ByteArrayOutputStream out = new ByteArrayOutputStream();
props.storeToXML(out, null, encoding);
@@ -89,6 +97,8 @@
* Test loadFromXML with a document that does not have an encoding declaration
*/
static void testLoadWithoutEncoding() throws IOException {
+ System.out.println("testLoadWithoutEncoding");
+
Properties expected = new Properties();
expected.put("foo", "bar");
@@ -107,10 +117,11 @@
}
}
- /**
- * Test loadFromXML with unsupported encoding
- */
- static void testLoadWithBadEncoding() throws IOException {
+ /**
+ * Test loadFromXML with unsupported encoding
+ */
+ static void testLoadWithBadEncoding() throws IOException {
+ System.out.println("testLoadWithBadEncoding");
String s = "<?xml version=\"1.0\" encoding=\"BAD\"?>" +
"<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">" +
"<properties>" +
@@ -128,6 +139,7 @@
* Test storeToXML with unsupported encoding
*/
static void testStoreWithBadEncoding() throws IOException {
+ System.out.println("testStoreWithBadEncoding");
Properties props = new Properties();
props.put("foo", "bar");
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -137,6 +149,26 @@
} catch (UnsupportedEncodingException expected) { }
}
+ /**
+ * Test loadFromXML with malformed documents
+ */
+ static void testLoadWithMalformedDoc(Path dir) throws IOException {
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.xml")) {
+ for (Path file: stream) {
+ System.out.println("testLoadWithMalformedDoc, file=" + file.getFileName());
+ try (InputStream in = Files.newInputStream(file)) {
+ Properties props = new Properties();
+ try {
+ props.loadFromXML(in);
+ throw new RuntimeException("InvalidPropertiesFormatException not thrown");
+ } catch (InvalidPropertiesFormatException x) {
+ System.out.println(x);
+ }
+ }
+ }
+ }
+ }
+
public static void main(String[] args) throws IOException {
testLoadAndStore("UTF-8");
@@ -145,6 +177,12 @@
testLoadWithBadEncoding();
testStoreWithBadEncoding();
+ // malformed documents
+ String src = System.getProperty("test.src");
+ String subdir = "invalidxml";
+ Path dir = (src == null) ? Paths.get(subdir) : Paths.get(src, subdir);
+ testLoadWithMalformedDoc(dir);
+
// re-run sanity test with security manager
Policy orig = Policy.getPolicy();
Policy p = new SimplePolicy(new RuntimePermission("setSecurityManager"),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/BadCase.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- XML tags are case sensitve, opening and closing tags must use same case -->
+
+<properties>
+<entry key="foo">bar</entry>
+<entry key="gus">baz</entry>
+</PROPERTIES>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/BadDocType.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE namevaluepairs SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- The root element for a XML properties document is properties -->
+
+<properties>
+<entry key="foo">bar</entry>
+<entry key="gus">baz</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/DTDRootNotMatch.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- XML tags are case sensitve, the root element should have been in lower case -->
+
+<Properties>
+<comment>comment</comment>
+<entry key="firstKey">value of the first key</entry>
+</Properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/IllegalComment.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- The dtd allows zero or one comment element -->
+
+<properties>
+<comment>comment1</comment>
+<comment>comment2</comment>
+<entry key="firstKey">value of the first key</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/IllegalEntry.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- The dtd requires 'entry' element, 'Entry' is illegal -->
+
+<properties>
+<comment>comment</comment>
+<Entry key="firstKey">value of the first key</Entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/IllegalEntry1.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- 'entry1' is illegal as per the dtd -->
+
+<properties>
+<comment>comment1</comment>
+
+<entry key="firstkey">value of the first key</entry>
+<entry1 key="secondkey">value of the second key</entry1>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/IllegalKeyAttribute.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- 'key' attribute is expected. 'key1' is mistyped -->
+
+<properties>
+<comment>comment1</comment>
+<entry key1="typo">value of the first key</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/NoClosingTag.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- XML elements must having a closing tag -->
+
+<properties>
+<entry key="foo">bar</entry>
+<entry key="gus">baz</entry>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/NoDocType.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<!-- An XML properties document has the DOCTYPE declaration with the properties root element -->
+
+<properties>
+<entry key="foo">bar</entry>
+<entry key="gus">baz</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/NoNamespaceSupport.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- namespace is not supported -->
+
+<javautil:properties xmlns:javautil="http://java.sun.com/java/util/Properties">
+<javautil:comment>comment1</javautil:comment>
+
+<javautil:entry key="firstkey">value of the first key</javautil:entry>
+<javautil:entry key="secondkey">value of the second key</javautil:entry>
+</javautil:properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/NoRoot.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- XML documents must have a root element -->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/NotQuoted.xml Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- XML attribute values must be quoted -->
+
+<properties>
+<entry key=foo>bar</entry>
+<entry key=gus>baz</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/README.txt Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,5 @@
+
+This directory contains test cases for the LoadAndStoreXML test case. Each file
+in this directory should be a malformed XML document that should cause the
+java.util.Properties loadFromXML method to throw InvalidPropertiesFormatException.
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/TimeZone/CLDRDisplayNamesTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8005471
+ * @run main/othervm -Djava.locale.providers=CLDR CLDRDisplayNamesTest
+ * @summary Make sure that localized time zone names of CLDR are used
+ * if specified.
+ */
+
+import java.util.*;
+import static java.util.TimeZone.*;
+
+public class CLDRDisplayNamesTest {
+ /*
+ * The first element is a language tag. The rest are localized
+ * display names of America/Los_Angeles copied from the CLDR
+ * resources data. If data change in CLDR, test data below will
+ * need to be changed accordingly.
+ *
+ * Generic names are NOT tested (until they are supported by API).
+ */
+ static final String[][] CLDR_DATA = {
+ {
+ "ja-JP",
+ "\u30a2\u30e1\u30ea\u30ab\u592a\u5e73\u6d0b\u6a19\u6e96\u6642",
+ "PST",
+ "\u30a2\u30e1\u30ea\u30ab\u592a\u5e73\u6d0b\u590f\u6642\u9593",
+ "PDT",
+ //"\u30a2\u30e1\u30ea\u30ab\u592a\u5e73\u6d0b\u6642\u9593",
+ //"PT"
+ },
+ {
+ "zh-CN",
+ "\u592a\u5e73\u6d0b\u6807\u51c6\u65f6\u95f4",
+ "PST",
+ "\u592a\u5e73\u6d0b\u590f\u4ee4\u65f6\u95f4",
+ "PDT",
+ //"\u7f8e\u56fd\u592a\u5e73\u6d0b\u65f6\u95f4",
+ //"PT"
+ },
+ {
+ "de-DE",
+ "Nordamerikanische Westk\u00fcsten-Winterzeit",
+ "PST",
+ "Nordamerikanische Westk\u00fcsten-Sommerzeit",
+ "PDT",
+ //"Nordamerikanische Westk\u00fcstenzeit",
+ //"PT"
+ },
+ };
+
+ public static void main(String[] args) {
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ int errors = 0;
+ for (String[] data : CLDR_DATA) {
+ Locale locale = Locale.forLanguageTag(data[0]);
+ for (int i = 1; i < data.length; i++) {
+ int style = ((i % 2) == 1) ? LONG : SHORT;
+ boolean daylight = (i == 3 || i == 4);
+ String name = tz.getDisplayName(daylight, style, locale);
+ if (!data[i].equals(name)) {
+ System.err.printf("error: got '%s' expected '%s' (style=%d, daylight=%s, locale=%s)%n",
+ name, data[i], style, daylight, locale);
+ errors++;
+ }
+ }
+ }
+ if (errors > 0) {
+ throw new RuntimeException("test failed");
+ }
+ }
+}
--- a/jdk/test/java/util/logging/LoggingDeadlock4.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/util/logging/LoggingDeadlock4.java Fri Dec 28 18:36:41 2012 -0800
@@ -23,11 +23,11 @@
/*
* @test
- * @bug 6977677
+ * @bug 6977677 8004928
* @summary Deadlock between LogManager.<clinit> and Logger.getLogger()
* @author Daniel D. Daugherty
- * @build LoggingDeadlock4
- * @run main/othervm/timeout=15 -Djava.awt.headless=true LoggingDeadlock4
+ * @compile -XDignore.symbol.file LoggingDeadlock4.java
+ * @run main/othervm/timeout=15 LoggingDeadlock4
*/
import java.util.concurrent.CountDownLatch;
@@ -39,21 +39,16 @@
private static CountDownLatch lmIsRunning = new CountDownLatch(1);
private static CountDownLatch logIsRunning = new CountDownLatch(1);
+ // Create a sun.util.logging.PlatformLogger$JavaLogger object
+ // that has to be redirected when the LogManager class
+ // is initialized. This can cause a deadlock between
+ // LogManager.<clinit> and Logger.getLogger().
+ private static final sun.util.logging.PlatformLogger log =
+ sun.util.logging.PlatformLogger.getLogger("java.util.logging");
+
public static void main(String[] args) {
System.out.println("main: LoggingDeadlock4 is starting.");
- // Loading the java.awt.Container class will create a
- // sun.util.logging.PlatformLogger$JavaLogger object
- // that has to be redirected when the LogManager class
- // is initialized. This can cause a deadlock between
- // LogManager.<clinit> and Logger.getLogger().
- try {
- Class.forName("java.awt.Container");
- } catch (ClassNotFoundException cnfe) {
- throw new RuntimeException("Test failed: could not load"
- + " java.awt.Container." + cnfe);
- }
-
Thread lmThread = new Thread("LogManagerThread") {
public void run() {
// let main know LogManagerThread is running
--- a/jdk/test/java/util/prefs/PrefsSpi.sh Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/java/util/prefs/PrefsSpi.sh Fri Dec 28 18:36:41 2012 -0800
@@ -87,17 +87,17 @@
case "`uname`" in Windows*|CYGWIN* ) CPS=';';; *) CPS=':';; esac
-Sys "$java" "${TESTVMOPTS}" "-cp" "$TESTCLASSES${CPS}extDir/PrefsSpi.jar" \
+Sys "$java" ${TESTVMOPTS} "-cp" "$TESTCLASSES${CPS}extDir/PrefsSpi.jar" \
-Djava.util.prefs.PreferencesFactory=StubPreferencesFactory \
-Djava.util.prefs.userRoot=. \
PrefsSpi "StubPreferences"
-Sys "$java" "${TESTVMOPTS}" "-cp" "$TESTCLASSES" \
+Sys "$java" ${TESTVMOPTS} "-cp" "$TESTCLASSES" \
-Djava.util.prefs.userRoot=. \
PrefsSpi "java.util.prefs.*"
-Sys "$java" "${TESTVMOPTS}" "-cp" "$TESTCLASSES${CPS}extDir/PrefsSpi.jar" \
+Sys "$java" ${TESTVMOPTS} "-cp" "$TESTCLASSES${CPS}extDir/PrefsSpi.jar" \
-Djava.util.prefs.userRoot=. \
PrefsSpi "StubPreferences"
-Sys "$java" "${TESTVMOPTS}" "-cp" "$TESTCLASSES" "-Djava.ext.dirs=extDir" \
+Sys "$java" ${TESTVMOPTS} "-cp" "$TESTCLASSES" "-Djava.ext.dirs=extDir" \
-Djava.util.prefs.userRoot=. \
PrefsSpi "StubPreferences"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/MBeanInfo/SerializationTest1.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2005, 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.
+ */
+
+/*
+ * @test
+ * @bug 6783290
+ * @summary Test correct reading of an empty Descriptor.
+ * @author Jaroslav Bachorik
+ * @run clean SerializationTest1
+ * @run build SerializationTest1
+ * @run main SerializationTest1
+ */
+
+import java.io.*;
+import javax.management.*;
+
+public class SerializationTest1 {
+ public static void main(String[] args) throws Exception {
+ MBeanInfo mi1 = new MBeanInfo("",
+ "",
+ new MBeanAttributeInfo[]{},
+ new MBeanConstructorInfo[]{},
+ new MBeanOperationInfo[]{},
+ new MBeanNotificationInfo[]{},
+ ImmutableDescriptor.EMPTY_DESCRIPTOR);
+
+ test(mi1);
+
+ MBeanFeatureInfo mfi2 = new MBeanFeatureInfo("",
+ "",
+ ImmutableDescriptor.EMPTY_DESCRIPTOR);
+
+ test(mfi2);
+ }
+
+ public static void test(Object obj) throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(obj);
+
+ final boolean[] failed = new boolean[]{false};
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ final ObjectInputStream ois = new ObjectInputStream(bais) {
+ protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+ System.out.println("*** " + desc.getName());
+ if (desc.getName().equals("[Ljava.lang.Object;")) { // javax.management.Descriptor fields
+ Thread.dumpStack();
+ for(StackTraceElement e : Thread.currentThread().getStackTrace()) { // checking for the deserialization location
+ if (e.getMethodName().equals("skipCustomData")) { // indicates the presence of unread values from the custom object serialization
+ failed[0] = true;
+ }
+ }
+ }
+ return super.resolveClass(desc); //To change body of generated methods, choose Tools | Templates.
+ }
+ };
+ Object newObj = ois.readObject();
+
+ if (failed[0]) {
+ throw new RuntimeException("Zero-length descriptor not read back");
+ }
+ }
+}
--- a/jdk/test/javax/management/remote/mandatory/connection/AddressableTest.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/javax/management/remote/mandatory/connection/AddressableTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -45,19 +45,36 @@
private static final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
+ private static boolean isProtocolSupported(String protocol) {
+ if (protocol.equals("rmi"))
+ return true;
+ if (protocol.equals("iiop")) {
+ try {
+ Class.forName("javax.management.remote.rmi._RMIConnectionImpl_Tie");
+ return true;
+ } catch (ClassNotFoundException x) { }
+ }
+ return false;
+ }
+
public static void main(String[] args) throws Exception {
System.out.println(">>> test the new interface Addressable.");
boolean ok = true;
for (int i = 0; i < protocols.length; i++) {
- try {
- test(protocols[i], prefixes[i]);
-
- System.out.println(">>> Test successed for "+protocols[i]);
- } catch (Exception e) {
- System.out.println(">>> Test failed for "+protocols[i]);
- e.printStackTrace(System.out);
- ok = false;
+ String protocol = protocols[i];
+ if (isProtocolSupported(protocol)) {
+ try {
+ test(protocol, prefixes[i]);
+ System.out.println(">>> Test successed for "+protocols[i]);
+ } catch (Exception e) {
+ System.out.println(">>> Test failed for "+protocols[i]);
+ e.printStackTrace(System.out);
+ ok = false;
+ }
+ } else {
+ System.out.format(">>> Test skipped for %s, protocol not supported%n",
+ protocol);
}
}
@@ -65,7 +82,7 @@
System.out.println(">>> All Test passed.");
} else {
System.out.println(">>> Some TESTs FAILED");
- System.exit(1);
+ throw new RuntimeException("See log for details");
}
}
--- a/jdk/test/javax/management/remote/mandatory/connection/CloseableTest.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/javax/management/remote/mandatory/connection/CloseableTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -42,7 +42,6 @@
import javax.management.remote.rmi.RMIIIOPServerImpl;
import javax.management.remote.rmi.RMIJRMPServerImpl;
import javax.management.remote.rmi.RMIServerImpl;
-import org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub;
public class CloseableTest {
private static final Class closeArray[] = {
@@ -51,26 +50,43 @@
RMIConnection.class,
RMIConnectionImpl.class,
RMIConnectionImpl_Stub.class,
- _RMIConnection_Stub.class,
RMIServerImpl.class,
RMIIIOPServerImpl.class,
RMIJRMPServerImpl.class
};
+
+ static int error;
+
+ static void test(Class<?> c) {
+ System.out.println("\nTest " + c);
+ if (Closeable.class.isAssignableFrom(c)) {
+ System.out.println("Test passed!");
+ } else {
+ error++;
+ System.out.println("Test failed!");
+ }
+ }
+
+ static void test(String cn) {
+ try {
+ test(Class.forName(cn));
+ } catch (ClassNotFoundException ignore) {
+ System.out.println("\n" + cn + " not tested.");
+ }
+ }
+
public static void main(String[] args) throws Exception {
System.out.println("Test that all the JMX Remote API classes that " +
"define\nthe method \"void close() throws " +
"IOException;\" extend\nor implement the " +
"java.io.Closeable interface.");
- int error = 0;
- for (Class c : closeArray) {
- System.out.println("\nTest " + c);
- if (Closeable.class.isAssignableFrom(c)) {
- System.out.println("Test passed!");
- } else {
- error++;
- System.out.println("Test failed!");
- }
+ for (Class<?> c : closeArray) {
+ test(c);
}
+
+ // Stub classes not present if RMI-IIOP not supported
+ test("org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub");
+
if (error > 0) {
final String msg = "\nTest FAILED! Got " + error + " error(s)";
System.out.println(msg);
--- a/jdk/test/javax/management/remote/mandatory/connection/ConnectionListenerNullTest.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/javax/management/remote/mandatory/connection/ConnectionListenerNullTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -39,33 +39,22 @@
import java.util.Map;
public class ConnectionListenerNullTest {
- static final boolean optionalFlag;
- static {
- Class genericClass = null;
+ static boolean isPresent(String cn) {
try {
- genericClass =
- Class.forName("javax.management.remote.generic.GenericConnector");
+ Class.forName(cn);
+ return true;
} catch (ClassNotFoundException x) {
- // NO optional package
+ return false;
}
- optionalFlag = (genericClass != null);
}
- final static String[] mandatoryList = {
- "service:jmx:rmi://", "service:jmx:iiop://"
- };
-
- final static String[] optionalList = {
- "service:jmx:jmxmp://"
- };
-
- public static int test(String[] urls) {
+ public static int test(String... urls) {
int errCount = 0;
for (int i=0;i<urls.length;i++) {
try {
final JMXServiceURL url = new JMXServiceURL(urls[i]);
final JMXConnector c =
- JMXConnectorFactory.newJMXConnector(url,(Map)null);
+ JMXConnectorFactory.newJMXConnector(url,(Map<String,String>)null);
final NotificationListener nl = null;
final NotificationFilter nf = null;
final Object h = null;
@@ -121,12 +110,19 @@
public static void main(String args[]) {
int errCount = 0;
- errCount += test(mandatoryList);
- if (optionalFlag) errCount += test(optionalList);
+
+ // mandatory
+ errCount += test("service:jmx:rmi://");
+
+ // optional
+ if (isPresent("javax.management.remote.rmi._RMIConnectionImpl_Tie"))
+ errCount += test("service:jmx:iiop://");
+ if (isPresent("javax.management.remote.generic.GenericConnector"))
+ errCount += test("service:jmx:jmxmp://");
+
if (errCount > 0) {
- System.err.println("ConnectionListenerNullTest failed: " +
- errCount + " error(s) reported.");
- System.exit(1);
+ throw new RuntimeException("ConnectionListenerNullTest failed: " +
+ errCount + " error(s) reported.");
}
System.out.println("ConnectionListenerNullTest passed.");
}
--- a/jdk/test/javax/management/remote/mandatory/connection/IIOPURLTest.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/javax/management/remote/mandatory/connection/IIOPURLTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -49,17 +49,24 @@
public static void main(String[] args) throws Exception {
JMXServiceURL inputAddr =
new JMXServiceURL("service:jmx:iiop://");
- JMXConnectorServer s =
- JMXConnectorServerFactory.newJMXConnectorServer(inputAddr, null,
- null);
+ JMXConnectorServer s;
+ try {
+ s = JMXConnectorServerFactory.newJMXConnectorServer(inputAddr, null, null);
+ } catch (java.net.MalformedURLException x) {
+ try {
+ Class.forName("javax.management.remote.rmi._RMIConnectionImpl_Tie");
+ throw new RuntimeException("MalformedURLException thrown but iiop appears to be supported");
+ } catch (ClassNotFoundException expected) { }
+ System.out.println("IIOP protocol not supported, test skipped");
+ return;
+ }
MBeanServer mbs = MBeanServerFactory.createMBeanServer();
mbs.registerMBean(s, new ObjectName("a:b=c"));
s.start();
JMXServiceURL outputAddr = s.getAddress();
if (!outputAddr.getURLPath().startsWith("/ior/IOR:")) {
- System.out.println("URL path should start with \"/ior/IOR:\": " +
- outputAddr);
- System.exit(1);
+ throw new RuntimeException("URL path should start with \"/ior/IOR:\": " +
+ outputAddr);
}
System.out.println("IIOP URL path looks OK: " + outputAddr);
JMXConnector c = JMXConnectorFactory.connect(outputAddr);
--- a/jdk/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -52,21 +52,27 @@
import com.sun.jmx.remote.util.EnvHelp;
public class IdleTimeoutTest {
+
+ static boolean isPresent(String cn) {
+ try {
+ Class.forName(cn);
+ return true;
+ } catch (ClassNotFoundException x) {
+ return false;
+ }
+ }
+
public static void main(String[] args) throws Exception {
boolean ok = true;
List protos;
if (args.length > 0)
protos = Arrays.asList(args);
else {
- protos =
- new ArrayList(Arrays.asList(new String[] {"rmi", "iiop"}));
- try {
- Class.forName("javax.management.remote.jmxmp." +
- "JMXMPConnectorServer");
+ protos = new ArrayList(Arrays.asList(new String[] {"rmi"}));
+ if (isPresent("javax.management.remote.rmi._RMIConnectionImpl_Tie"))
+ protos.add("iiop");
+ if (isPresent("javax.management.remote.jmxmp.JMXMPConnectorServer"))
protos.add("jmxmp");
- } catch (ClassNotFoundException e) {
- // OK: Optional JMXMP support is not present
- }
}
for (Iterator it = protos.iterator(); it.hasNext(); ) {
String proto = (String) it.next();
@@ -81,13 +87,13 @@
}
}
if (!ok) {
- System.out.println("SOME TESTS FAILED");
- System.exit(1);
+ throw new RuntimeException("Some tests failed - see log for details");
}
}
private static long getIdleTimeout(MBeanServer mbs, JMXServiceURL url)
- throws Exception {
+ throws Exception
+ {
JMXConnectorServer server =
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
server.start();
--- a/jdk/test/javax/management/remote/mandatory/connection/MultiThreadDeadLockTest.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/javax/management/remote/mandatory/connection/MultiThreadDeadLockTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -253,4 +253,3 @@
System.out.println("===Leave the method: " + m);
}
}
-
--- a/jdk/test/javax/management/remote/mandatory/connectorServer/SetMBeanServerForwarder.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/javax/management/remote/mandatory/connectorServer/SetMBeanServerForwarder.java Fri Dec 28 18:36:41 2012 -0800
@@ -40,27 +40,16 @@
public class SetMBeanServerForwarder {
- static final boolean optionalFlag;
- static {
- Class genericClass = null;
+ static boolean isPresent(String cn) {
try {
- genericClass =
- Class.forName("javax.management.remote.generic.GenericConnector");
+ Class.forName(cn);
+ return true;
} catch (ClassNotFoundException x) {
- // NO optional package
+ return false;
}
- optionalFlag = (genericClass != null);
}
- final static String[] mandatoryList = {
- "service:jmx:rmi://", "service:jmx:iiop://"
- };
-
- final static String[] optionalList = {
- "service:jmx:jmxmp://"
- };
-
- public static int test(String[] urls) {
+ public static int test(String... urls) {
int errorCount = 0;
for (int i=0;i<urls.length;i++) {
try {
@@ -241,12 +230,19 @@
public static void main(String args[]) {
int errCount = 0;
- errCount += test(mandatoryList);
- if (optionalFlag) errCount += test(optionalList);
+
+ // mandatory
+ errCount += test("service:jmx:rmi://");
+
+ // optional
+ if (isPresent("javax.management.remote.rmi._RMIConnectionImpl_Tie"))
+ errCount += test("service:jmx:iiop://");
+ if (isPresent("javax.management.remote.generic.GenericConnector"))
+ errCount += test("service:jmx:jmxmp://");
+
if (errCount > 0) {
- System.err.println("SetMBeanServerForwarder failed: " +
- errCount + " error(s) reported.");
- System.exit(1);
+ throw new RuntimeException("SetMBeanServerForwarder failed: " +
+ errCount + " error(s) reported.");
}
System.out.println("SetMBeanServerForwarder passed.");
}
--- a/jdk/test/javax/management/remote/mandatory/loading/MissingClassTest.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/javax/management/remote/mandatory/loading/MissingClassTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -70,7 +70,6 @@
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
-import org.omg.CORBA.MARSHAL;
public class MissingClassTest {
private static final int NNOTIFS = 50;
@@ -84,6 +83,15 @@
private static final Object unserializableObject = Thread.currentThread();
+ private static boolean isInstance(Object o, String cn) {
+ try {
+ Class<?> c = Class.forName(cn);
+ return c.isInstance(o);
+ } catch (ClassNotFoundException x) {
+ return false;
+ }
+ }
+
public static void main(String[] args) throws Exception {
System.out.println("Test that the client or server end of a " +
"connection does not fail if sent an object " +
@@ -118,8 +126,7 @@
if (ok)
System.out.println("Test passed");
else {
- System.out.println("TEST FAILED");
- System.exit(1);
+ throw new RuntimeException("TEST FAILED");
}
}
@@ -133,7 +140,7 @@
JMXConnectorServer cs;
JMXServiceURL url = new JMXServiceURL(proto, null, 0);
- Map serverMap = new HashMap();
+ Map<String,Object> serverMap = new HashMap<>();
serverMap.put(JMXConnectorServerFactory.DEFAULT_CLASS_LOADER,
serverLoader);
@@ -151,7 +158,7 @@
}
cs.start();
JMXServiceURL addr = cs.getAddress();
- Map clientMap = new HashMap();
+ Map<String,Object> clientMap = new HashMap<>();
clientMap.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,
clientLoader);
@@ -174,7 +181,7 @@
ok = false;
} catch (IOException e) {
Throwable cause = e.getCause();
- if (cause instanceof MARSHAL) // see CR 4935098
+ if (isInstance(cause, "org.omg.CORBA.MARSHAL")) // see CR 4935098
cause = cause.getCause();
if (cause instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException wrapping " +
@@ -188,7 +195,7 @@
}
System.out.println("Doing queryNames to ensure connection alive");
- Set names = mbsc.queryNames(null, null);
+ Set<ObjectName> names = mbsc.queryNames(null, null);
System.out.println("queryNames returned " + names);
System.out.println("Provoke exception of unknown class");
@@ -198,7 +205,7 @@
ok = false;
} catch (IOException e) {
Throwable wrapped = e.getCause();
- if (wrapped instanceof MARSHAL) // see CR 4935098
+ if (isInstance(wrapped, "org.omg.CORBA.MARSHAL")) // see CR 4935098
wrapped = wrapped.getCause();
if (wrapped instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException wrapping " +
@@ -251,7 +258,7 @@
ok = false;
} catch (IOException e) {
Throwable cause = e.getCause();
- if (cause instanceof MARSHAL) // see CR 4935098
+ if (isInstance(cause, "org.omg.CORBA.MARSHAL")) // see CR 4935098
cause = cause.getCause();
if (cause instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException " +
@@ -584,15 +591,13 @@
try {
new ObjectOutputStream(new ByteArrayOutputStream())
.writeObject(tricky);
- System.out.println("TEST INCORRECT: tricky notif is " +
- "serializable");
- System.exit(1);
+ throw new RuntimeException("TEST INCORRECT: tricky notif is " +
+ "serializable");
} catch (NotSerializableException e) {
// OK: tricky notif is not serializable
} catch (IOException e) {
- System.out.println("TEST INCORRECT: tricky notif " +
- "serialization check failed");
- System.exit(1);
+ throw new RuntimeException("TEST INCORRECT: tricky notif " +
+ "serialization check failed");
}
/* Now shuffle an imaginary deck of cards where K, U, T, and
@@ -629,12 +634,11 @@
}
if (knownCount != 0 || unknownCount != 0
|| trickyCount != 0 || boringCount != 0) {
- System.out.println("TEST INCORRECT: Shuffle failed: " +
+ throw new RuntimeException("TEST INCORRECT: Shuffle failed: " +
"known=" + knownCount +" unknown=" +
unknownCount + " tricky=" + trickyCount +
" boring=" + boringCount +
" deal=" + notifList);
- System.exit(1);
}
String notifs = notifList.toString();
System.out.println("Shuffle: " + notifs);
@@ -646,10 +650,8 @@
case 't': n = tricky; break;
case 'b': n = boring; break;
default:
- System.out.println("TEST INCORRECT: Bad shuffle char: " +
- notifs.charAt(i));
- System.exit(1);
- throw new Error();
+ throw new RuntimeException("TEST INCORRECT: Bad shuffle char: " +
+ notifs.charAt(i));
}
sendNotification(n);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/remote/mandatory/notif/ConcurrentModificationTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/*
+ * @test
+ * @bug 7120365
+ * @summary test on Concurrent Modification
+ * @author Shanliang JIANG
+ * @run main ConcurrentModificationTest
+ */
+
+import java.net.MalformedURLException;
+import java.util.ConcurrentModificationException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerFactory;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ *
+ */
+public class ConcurrentModificationTest {
+ private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
+ private static int number = 100;
+
+ private static final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
+ private static ObjectName delegateName;
+ private static ObjectName[] timerNames = new ObjectName[number];
+ private static NotificationListener[] listeners = new NotificationListener[number];
+
+ private static Throwable uncaughtException = null;
+
+ public static void main(String[] args) throws Exception {
+ System.out.println(">>> test on Concurrent Modification.");
+
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ e.printStackTrace();
+ if (e instanceof ConcurrentModificationException) {
+ uncaughtException = e;
+ }
+ }
+ });
+
+ delegateName = new ObjectName("JMImplementation:type=MBeanServerDelegate");
+ for (int i=0; i<number; i++) {
+ timerNames[i] = new ObjectName("MBean:name=Timer"+i);
+ listeners[i] = new NotificationListener() {
+ @Override
+ public void handleNotification(Notification notification, Object handback) {
+ // nothing
+ }
+ };
+ }
+ String errors = "";
+
+ for (int i = 0; i < protocols.length; i++) {
+ uncaughtException = null;
+ System.out.println(">>> Test for protocol " + protocols[i]);
+ test(protocols[i]);
+ if (uncaughtException != null) {
+ if ("".equals(errors)) {
+ errors = "Failed to " + protocols[i] + ": "+uncaughtException;
+ } else {
+ errors = errors+", failed to " + protocols[i] + ": "+uncaughtException;
+ }
+ System.out.println(">>> FAILED for protocol " + protocols[i]);
+ } else {
+ System.out.println(">>> PASSED for protocol " + protocols[i]);
+ }
+ }
+
+ if ("".equals(errors)) {
+ System.out.println("All Passed!");
+ } else {
+ System.out.println("!!!!!! Failed.");
+
+ throw new RuntimeException(errors);
+ }
+ }
+
+ private static void test(String proto) throws Exception {
+ JMXServiceURL u = new JMXServiceURL(proto, null, 0);
+ JMXConnectorServer server;
+ JMXConnector client;
+
+ try {
+ server =
+ JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
+ server.start();
+ JMXServiceURL addr = server.getAddress();
+ client = JMXConnectorFactory.connect(addr, null);
+ } catch (MalformedURLException e) {
+ System.out.println(">>> not support: " + proto);
+ return;
+ }
+
+ final MBeanServerConnection mserver = client.getMBeanServerConnection();
+
+ int count = 0;
+ boolean adding = true;
+ while (uncaughtException == null && count++ < 10) {
+ for (int i = 0; i < number; i++) {
+ listenerOp(mserver, listeners[i], adding);
+ mbeanOp(mserver, timerNames[i], adding);
+ }
+ adding = !adding;
+ }
+
+ if (uncaughtException != null) { // clean
+ for (int i = 0; i < number; i++) {
+ try {
+ mbeanOp(mserver, timerNames[i], false);
+ } catch (Exception e) {
+ }
+ }
+ }
+ client.close();
+ server.stop();
+ }
+
+ private static void mbeanOp(MBeanServerConnection mserver, ObjectName name, boolean adding)
+ throws Exception {
+ if (adding) {
+ mserver.createMBean("javax.management.timer.Timer", name);
+ } else {
+ mserver.unregisterMBean(name);
+ }
+ }
+
+ private static void listenerOp(MBeanServerConnection mserver, NotificationListener listener, boolean adding)
+ throws Exception {
+ if (adding) {
+ mserver.addNotificationListener(delegateName, listener, null, null);
+ } else {
+ mserver.removeNotificationListener(delegateName, listener);
+ }
+ }
+}
\ No newline at end of file
--- a/jdk/test/javax/management/remote/mandatory/provider/ProviderTest.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/javax/management/remote/mandatory/provider/ProviderTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -49,13 +49,13 @@
import provider.JMXConnectorProviderImpl;
import provider.JMXConnectorServerProviderImpl;
public class ProviderTest {
+
public static void main(String[] args) throws Exception {
System.out.println("Starting ProviderTest");
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
- //First do the test with a protocol handled by Service providers
- JMXServiceURL url =
- new JMXServiceURL("service:jmx:rmi://");
+ // First do the test with a protocol handled by Service providers
+ JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
dotest(url, mbs);
boolean clientCalled = provider.JMXConnectorProviderImpl.called();
@@ -66,16 +66,22 @@
System.out.println("Client provider not called");
if (!serverCalled)
System.out.println("Server provider not called");
- System.out.println("Test Failed");
- System.exit(1);
+ throw new RuntimeException("Test failed - see log for details");
}
- //The Service Provider doesn't handle IIOP. Default providers MUST
- //be called.
- url =
- new JMXServiceURL("service:jmx:iiop://");
-
- dotest(url, mbs);
+ // The Service Provider doesn't handle IIOP. Default providers MUST
+ // be called, which may or may not support IIOP.
+ url = new JMXServiceURL("service:jmx:iiop://");
+ try {
+ dotest(url, mbs);
+ } catch (MalformedURLException e) {
+ try {
+ Class.forName("javax.management.remote.rmi._RMIConnectionImpl_Tie");
+ e.printStackTrace(System.out);
+ throw new RuntimeException("MalformedURLException throw but IIOP appears to be supported");
+ } catch (ClassNotFoundException expected) { }
+ System.out.println("MalformedURLException thrown, IIOP transport not supported");
+ }
// Unsupported protocol.
JMXConnectorServer server = null;
@@ -87,31 +93,19 @@
JMXConnectorServerFactory.newJMXConnectorServer(url,
null,
mbs);
- System.out.println("Exception not thrown.");
- System.exit(1);
- }catch(MalformedURLException e) {
+ throw new RuntimeException("Exception not thrown.");
+ } catch (MalformedURLException e) {
System.out.println("Expected MalformedURLException thrown.");
}
- catch(Exception e) {
- e.printStackTrace();
- System.out.println("Exception thrown : " + e);
- System.exit(1);
- }
try {
client =
JMXConnectorFactory.newJMXConnector(url,
null);
- System.out.println("Exception not thrown.");
- System.exit(1);
- }catch(MalformedURLException e) {
+ throw new RuntimeException("Exception not thrown.");
+ } catch (MalformedURLException e) {
System.out.println("Expected MalformedURLException thrown.");
}
- catch(Exception e) {
- e.printStackTrace();
- System.out.println("Exception thrown : " + e);
- System.exit(1);
- }
//JMXConnectorProviderException
url =
@@ -121,60 +115,34 @@
JMXConnectorServerFactory.newJMXConnectorServer(url,
null,
mbs);
- System.out.println("Exception not thrown.");
- System.exit(1);
- }catch(JMXProviderException e) {
+ throw new RuntimeException("Exception not thrown.");
+ } catch(JMXProviderException e) {
System.out.println("Expected JMXProviderException thrown.");
}
- catch(Exception e) {
- e.printStackTrace();
- System.out.println("Exception thrown : " + e);
- System.exit(1);
- }
try {
client =
JMXConnectorFactory.newJMXConnector(url,
null);
- System.out.println("Exception not thrown.");
- System.exit(1);
+ throw new RuntimeException("Exception not thrown.");
}catch(JMXProviderException e) {
System.out.println("Expected JMXProviderException thrown.");
}
- catch(Exception e) {
- e.printStackTrace();
- System.out.println("Exception thrown : " + e);
- System.exit(1);
- }
System.out.println("Test OK");
- return;
}
private static void dotest(JMXServiceURL url, MBeanServer mbs)
throws Exception {
JMXConnectorServer server = null;
JMXConnector client = null;
- try {
- server =
- JMXConnectorServerFactory.newJMXConnectorServer(url,
- null,
- mbs);
- }catch(IllegalArgumentException e) {
- e.printStackTrace();
- System.exit(1);
- }
+
+ server = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
server.start();
JMXServiceURL outputAddr = server.getAddress();
System.out.println("Server started ["+ outputAddr+ "]");
- try {
- client =
- JMXConnectorFactory.newJMXConnector(outputAddr, null);
- }catch(IllegalArgumentException e) {
- e.printStackTrace();
- System.exit(1);
- }
+ client = JMXConnectorFactory.newJMXConnector(outputAddr, null);
client.connect();
System.out.println("Client connected");
--- a/jdk/test/javax/management/remote/mandatory/serverError/JMXServerErrorTest.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/javax/management/remote/mandatory/serverError/JMXServerErrorTest.java Fri Dec 28 18:36:41 2012 -0800
@@ -120,7 +120,7 @@
try {
cs=JMXConnectorServerFactory.newJMXConnectorServer(jurl,null,kbs);
} catch (MalformedURLException m) {
- if ("jmxmp".equals(jurl.getProtocol())) {
+ if ("jmxmp".equals(jurl.getProtocol()) || "iiop".equals(jurl.getProtocol())) {
// OK, we may not have this in the classpath...
System.out.println("WARNING: Skipping protocol: " + jurl);
return;
--- a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopDoSomething.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/management/jmxremote/startstop/JMXStartStopDoSomething.java Fri Dec 28 18:36:41 2012 -0800
@@ -41,7 +41,7 @@
System.err.println("Lock is too old. Aborting");
return;
}
- Thread.sleep(1);
+ Thread.sleep(500);
}
} catch (Throwable e) {
--- a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.sh Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.sh Fri Dec 28 18:36:41 2012 -0800
@@ -43,7 +43,7 @@
_compile(){
- if [ ! -e ${_testclasses} ]
+ if [ ! -d ${_testclasses} ]
then
mkdir -p ${_testclasses}
fi
@@ -53,7 +53,7 @@
# Compile testcase
${TESTJAVA}/bin/javac -d ${_testclasses} JMXStartStopDoSomething.java JMXStartStopTest.java
- if [ ! -e ${_testclasses}/JMXStartStopTest.class ]
+ if [ ! -f ${_testclasses}/JMXStartStopTest.class ]
then
echo "ERROR: Can't compile"
exit -1
@@ -63,15 +63,22 @@
_app_start(){
${TESTJAVA}/bin/java ${TESTVMOPTS} $* -cp ${_testclasses} JMXStartStopDoSomething >> ${_logname} 2>&1 &
- npid=`_get_pid`
- if [ "${npid}" = "" ]
- then
- echo "ERROR: Test app not started"
- if [ "${_jtreg}" = "yes" ]
+ x=0
+ while [ ! -f ${_lockFileName} ]
+ do
+ if [ $x -gt 20 ]
then
- exit -1
- fi
- fi
+ echo "ERROR: Test app not started"
+ if [ "${_jtreg}" = "yes" ]
+ then
+ exit -1
+ fi
+ fi
+
+ echo "Waiting JMXStartStopDoSomething to start: $x"
+ x=`expr $x + 1`
+ sleep 1
+ done
}
_get_pid(){
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/net/www/http/KeepAliveStream/InfiniteLoop.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/*
+ * @test
+ * @bug 8004863
+ * @summary Checks for proper close code in KeepAliveStream
+ */
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import java.io.*;
+import java.net.*;
+import java.util.concurrent.Phaser;
+
+// Racey test, will not always fail, but if it does then we have a problem.
+
+public class InfiniteLoop {
+
+ public static void main(String[] args) throws Exception {
+ HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);
+ server.createContext("/test/InfiniteLoop", new RespHandler());
+ server.start();
+ try {
+ InetSocketAddress address = server.getAddress();
+ URL url = new URL("http://localhost:" + address.getPort()
+ + "/test/InfiniteLoop");
+ final Phaser phaser = new Phaser(2);
+ for (int i=0; i<10; i++) {
+ HttpURLConnection uc = (HttpURLConnection)url.openConnection();
+ final InputStream is = uc.getInputStream();
+ final Thread thread = new Thread() {
+ public void run() {
+ try {
+ phaser.arriveAndAwaitAdvance();
+ while (is.read() != -1)
+ Thread.sleep(50);
+ } catch (Exception x) { x.printStackTrace(); }
+ }};
+ thread.start();
+ phaser.arriveAndAwaitAdvance();
+ is.close();
+ System.out.println("returned from close");
+ thread.join();
+ }
+ } finally {
+ server.stop(0);
+ }
+ }
+
+ static class RespHandler implements HttpHandler {
+ static final int RESP_LENGTH = 32 * 1024;
+ @Override
+ public void handle(HttpExchange t) throws IOException {
+ InputStream is = t.getRequestBody();
+ byte[] ba = new byte[8192];
+ while(is.read(ba) != -1);
+
+ t.sendResponseHeaders(200, RESP_LENGTH);
+ try (OutputStream os = t.getResponseBody()) {
+ os.write(new byte[RESP_LENGTH]);
+ }
+ t.close();
+ }
+ }
+}
--- a/jdk/test/sun/rmi/runtime/Log/6409194/NoConsoleOutput.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/rmi/runtime/Log/6409194/NoConsoleOutput.java Fri Dec 28 18:36:41 2012 -0800
@@ -64,7 +64,7 @@
// (neither on standard output, nor on standard err streams).
JavaVM vm = new JavaVM(DoRMIStuff.class.getName(),
"-Djava.util.logging.config.file=" + loggingPropertiesFile,
- "", out, err, false);
+ "", out, err);
vm.start();
vm.getVM().waitFor();
--- a/jdk/test/sun/security/krb5/auto/DynamicKeytab.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/security/krb5/auto/DynamicKeytab.java Fri Dec 28 18:36:41 2012 -0800
@@ -110,11 +110,13 @@
throw new Exception("Should not success");
} catch (GSSException gsse) {
System.out.println(gsse);
- KrbException ke = (KrbException)gsse.getCause();
- if (ke.returnCode() != Krb5.KRB_AP_ERR_BADKEYVER) {
- throw new Exception("Not expected failure code: " +
- ke.returnCode());
- }
+ // Since 7197159, different kvno is accepted, this return code
+ // will never be thrown out again.
+ //KrbException ke = (KrbException)gsse.getCause();
+ //if (ke.returnCode() != Krb5.KRB_AP_ERR_BADKEYVER) {
+ // throw new Exception("Not expected failure code: " +
+ // ke.returnCode());
+ //}
}
// Test 8: an empty KDC means revoke all
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/KvnoNA.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+/*
+ * @test
+ * @bug 7197159
+ * @compile -XDignore.symbol.file KvnoNA.java
+ * @run main/othervm KvnoNA
+ * @summary accept different kvno if there no match
+ */
+
+import org.ietf.jgss.GSSException;
+import sun.security.jgss.GSSUtil;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.PrincipalName;
+import sun.security.krb5.internal.ktab.KeyTab;
+import sun.security.krb5.internal.Krb5;
+
+public class KvnoNA {
+
+ public static void main(String[] args)
+ throws Exception {
+
+ OneKDC kdc = new OneKDC(null);
+ kdc.writeJAASConf();
+
+ // In KDC, it's 2
+ char[] pass = "pass2".toCharArray();
+ kdc.addPrincipal(OneKDC.SERVER, pass);
+
+ // In ktab, kvno is 1 or 3, 3 has the same password
+ KeyTab ktab = KeyTab.create(OneKDC.KTAB);
+ PrincipalName p = new PrincipalName(
+ OneKDC.SERVER+"@"+OneKDC.REALM, PrincipalName.KRB_NT_SRV_HST);
+ ktab.addEntry(p, "pass1".toCharArray(), 1, true);
+ ktab.addEntry(p, "pass2".toCharArray(), 3, true);
+ ktab.save();
+
+ Context c, s;
+
+ c = Context.fromUserPass("dummy", "bogus".toCharArray(), false);
+ s = Context.fromJAAS("server");
+
+ c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
+ s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
+
+ Context.handshake(c, s);
+
+ s.dispose();
+ c.dispose();
+ }
+}
--- a/jdk/test/sun/security/krb5/auto/MoreKvno.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/security/krb5/auto/MoreKvno.java Fri Dec 28 18:36:41 2012 -0800
@@ -23,8 +23,7 @@
/*
* @test
- * @bug 6893158
- * @bug 6907425
+ * @bug 6893158 6907425 7197159
* @run main/othervm MoreKvno
* @summary AP_REQ check should use key version number
*/
@@ -69,11 +68,13 @@
go(OneKDC.SERVER, "com.sun.security.jgss.krb5.accept", pass);
throw new Exception("This test should fail");
} catch (GSSException gsse) {
- KrbException ke = (KrbException)gsse.getCause();
- if (ke.returnCode() != Krb5.KRB_AP_ERR_BADKEYVER) {
- throw new Exception("Not expected failure code: " +
- ke.returnCode());
- }
+ // Since 7197159, different kvno is accepted, this return code
+ // will never be thrown out again.
+ //KrbException ke = (KrbException)gsse.getCause();
+ //if (ke.returnCode() != Krb5.KRB_AP_ERR_BADKEYVER) {
+ // throw new Exception("Not expected failure code: " +
+ // ke.returnCode());
+ //}
}
}
--- a/jdk/test/sun/security/provider/certpath/DisabledAlgorithms/CPBuilder.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/security/provider/certpath/DisabledAlgorithms/CPBuilder.java Fri Dec 28 18:36:41 2012 -0800
@@ -21,6 +21,9 @@
* questions.
*/
+// This test case relies on updated static security property, no way to re-use
+// security property in samevm/agentvm mode.
+
/**
* @test
*
@@ -392,6 +395,9 @@
}
public static void main(String args[]) throws Exception {
+ // reset the security property to make sure that the algorithms
+ // and keys used in this test are not disabled.
+ Security.setProperty("jdk.certpath.disabledAlgorithms", "MD2");
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
--- a/jdk/test/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorEndEntity.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorEndEntity.java Fri Dec 28 18:36:41 2012 -0800
@@ -21,6 +21,9 @@
* questions.
*/
+// This test case relies on updated static security property, no way to re-use
+// security property in samevm/agentvm mode.
+
/**
* @test
*
@@ -28,7 +31,7 @@
* @summary Disable MD2 support.
* New CertPathValidatorException.BasicReason enum constant for
* constrained algorithm.
- *
+ * @run main/othervm CPValidatorEndEntity
* @author Xuelei Fan
*/
@@ -313,6 +316,10 @@
}
public static void main(String args[]) throws Exception {
+ // reset the security property to make sure that the algorithms
+ // and keys used in this test are not disabled.
+ Security.setProperty("jdk.certpath.disabledAlgorithms", "MD2");
+
try {
validate(endentiry_SHA1withRSA_1024_1024,
intermediate_SHA1withRSA_1024_1024);
--- a/jdk/test/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorIntermediate.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorIntermediate.java Fri Dec 28 18:36:41 2012 -0800
@@ -21,6 +21,9 @@
* questions.
*/
+// This test case relies on updated static security property, no way to re-use
+// security property in samevm/agentvm mode.
+
/**
* @test
*
@@ -28,7 +31,7 @@
* @summary Disable MD2 support
* new CertPathValidatorException.BasicReason enum constant for
* constrained algorithm
- *
+ * @run main/othervm CPValidatorIntermediate
* @author Xuelei Fan
*/
@@ -212,6 +215,10 @@
}
public static void main(String args[]) throws Exception {
+ // reset the security property to make sure that the algorithms
+ // and keys used in this test are not disabled.
+ Security.setProperty("jdk.certpath.disabledAlgorithms", "MD2");
+
try {
validate(intermediate_SHA1withRSA_1024_1024);
validate(intermediate_SHA1withRSA_1024_512);
--- a/jdk/test/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorTrustAnchor.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/security/provider/certpath/DisabledAlgorithms/CPValidatorTrustAnchor.java Fri Dec 28 18:36:41 2012 -0800
@@ -21,6 +21,9 @@
* questions.
*/
+// This test case relies on updated static security property, no way to re-use
+// security property in samevm/agentvm mode.
+
/**
* @test
*
@@ -28,7 +31,7 @@
* @summary Disable MD2 support
* new CertPathValidatorException.BasicReason enum constant for
* constrained algorithm
- *
+ * @run main/othervm CPValidatorTrustAnchor
* @author Xuelei Fan
*/
@@ -133,6 +136,10 @@
}
public static void main(String args[]) throws Exception {
+ // reset the security property to make sure that the algorithms
+ // and keys used in this test are not disabled.
+ Security.setProperty("jdk.certpath.disabledAlgorithms", "MD2");
+
try {
validate(trustAnchor_SHA1withRSA_1024);
validate(trustAnchor_SHA1withRSA_512);
--- a/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/ClientHandshaker/RSAExport.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/ClientHandshaker/RSAExport.java Fri Dec 28 18:36:41 2012 -0800
@@ -21,14 +21,14 @@
* questions.
*/
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
/*
* @test
* @bug 6690018
* @summary RSAClientKeyExchange NullPointerException
* @run main/othervm RSAExport
- *
- * SunJSSE does not support dynamic system properties, no way to re-use
- * system properties in samevm/agentvm mode.
*/
/*
@@ -199,6 +199,7 @@
import java.io.*;
import java.net.*;
+import java.security.Security;
import java.security.KeyStore;
import java.security.KeyFactory;
import java.security.cert.Certificate;
@@ -415,6 +416,10 @@
volatile Exception clientException = null;
public static void main(String[] args) throws Exception {
+ // reset the security property to make sure that the algorithms
+ // and keys used in this test are not disabled.
+ Security.setProperty("jdk.certpath.disabledAlgorithms", "MD2");
+
if (debug)
System.setProperty("javax.net.debug", "all");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/TLSv12/DisabledShortRSAKeys.java Fri Dec 28 18:36:41 2012 -0800
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2012, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 7109274
+ * @summary Consider disabling support for X.509 certificates with RSA keys
+ * less than 1024 bits
+ *
+ * @run main/othervm DisabledShortRSAKeys PKIX TLSv1.2
+ * @run main/othervm DisabledShortRSAKeys SunX509 TLSv1.2
+ * @run main/othervm DisabledShortRSAKeys PKIX TLSv1.1
+ * @run main/othervm DisabledShortRSAKeys SunX509 TLSv1.1
+ * @run main/othervm DisabledShortRSAKeys PKIX TLSv1
+ * @run main/othervm DisabledShortRSAKeys SunX509 TLSv1
+ * @run main/othervm DisabledShortRSAKeys PKIX SSLv3
+ * @run main/othervm DisabledShortRSAKeys SunX509 SSLv3
+ */
+
+import java.net.*;
+import java.util.*;
+import java.io.*;
+import javax.net.ssl.*;
+import java.security.Security;
+import java.security.KeyStore;
+import java.security.KeyFactory;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.spec.*;
+import java.security.interfaces.*;
+import sun.misc.BASE64Decoder;
+
+
+public class DisabledShortRSAKeys {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = true;
+
+ /*
+ * Where do we find the keystores?
+ */
+ // Certificates and key used in the test.
+ static String trustedCertStr =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIICkjCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+ "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+ "MTEwODE5MDE1MjE5WhcNMzIwNzI5MDE1MjE5WjA7MQswCQYDVQQGEwJVUzENMAsG\n" +
+ "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwgZ8wDQYJ\n" +
+ "KoZIhvcNAQEBBQADgY0AMIGJAoGBAM8orG08DtF98TMSscjGsidd1ZoN4jiDpi8U\n" +
+ "ICz+9dMm1qM1d7O2T+KH3/mxyox7Rc2ZVSCaUD0a3CkhPMnlAx8V4u0H+E9sqso6\n" +
+ "iDW3JpOyzMExvZiRgRG/3nvp55RMIUV4vEHOZ1QbhuqG4ebN0Vz2DkRft7+flthf\n" +
+ "vDld6f5JAgMBAAGjgaUwgaIwHQYDVR0OBBYEFLl81dnfp0wDrv0OJ1sxlWzH83Xh\n" +
+ "MGMGA1UdIwRcMFqAFLl81dnfp0wDrv0OJ1sxlWzH83XhoT+kPTA7MQswCQYDVQQG\n" +
+ "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" +
+ "Y2WCAQAwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEE\n" +
+ "BQADgYEALlgaH1gWtoBZ84EW8Hu6YtGLQ/L9zIFmHonUPZwn3Pr//icR9Sqhc3/l\n" +
+ "pVTxOINuFHLRz4BBtEylzRIOPzK3tg8XwuLb1zd0db90x3KBCiAL6E6cklGEPwLe\n" +
+ "XYMHDn9eDsaq861Tzn6ZwzMgw04zotPMoZN0mVd/3Qca8UJFucE=\n" +
+ "-----END CERTIFICATE-----";
+
+ static String targetCertStr =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIICNDCCAZ2gAwIBAgIBDDANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" +
+ "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" +
+ "MTExMTA3MTM1NTUyWhcNMzEwNzI1MTM1NTUyWjBPMQswCQYDVQQGEwJVUzENMAsG\n" +
+ "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxEjAQBgNV\n" +
+ "BAMTCWxvY2FsaG9zdDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC3Pb49OSPfOD2G\n" +
+ "HSXFCFx1GJEZfqG9ZUf7xuIi/ra5dLjPGAaoY5QF2QOa8VnOriQCXDfyXHxsuRnE\n" +
+ "OomxL7EVAgMBAAGjeDB2MAsGA1UdDwQEAwID6DAdBgNVHQ4EFgQUXNCJK3/dtCIc\n" +
+ "xb+zlA/JINlvs/MwHwYDVR0jBBgwFoAUuXzV2d+nTAOu/Q4nWzGVbMfzdeEwJwYD\n" +
+ "VR0lBCAwHgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAzANBgkqhkiG9w0B\n" +
+ "AQQFAAOBgQB2qIDUxA2caMPpGtUACZAPRUtrGssCINIfItETXJZCx/cRuZ5sP4D9\n" +
+ "N1acoNDn0hCULe3lhXAeTC9NZ97680yJzregQMV5wATjo1FGsKY30Ma+sc/nfzQW\n" +
+ "+h/7RhYtoG0OTsiaDCvyhI6swkNJzSzrAccPY4+ZgU8HiDLzZTmM3Q==\n" +
+ "-----END CERTIFICATE-----";
+
+ // Private key in the format of PKCS#8, key size is 512 bits.
+ static String targetPrivateKey =
+ "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAtz2+PTkj3zg9hh0l\n" +
+ "xQhcdRiRGX6hvWVH+8biIv62uXS4zxgGqGOUBdkDmvFZzq4kAlw38lx8bLkZxDqJ\n" +
+ "sS+xFQIDAQABAkByx/5Oo2hQ/w2q4L8z+NTRlJ3vdl8iIDtC/4XPnfYfnGptnpG6\n" +
+ "ZThQRvbMZiai0xHQPQMszvAHjZVme1eDl3EBAiEA3aKJHynPVCEJhpfCLWuMwX5J\n" +
+ "1LntwJO7NTOyU5m8rPECIQDTpzn5X44r2rzWBDna/Sx7HW9IWCxNgUD2Eyi2nA7W\n" +
+ "ZQIgJerEorw4aCAuzQPxiGu57PB6GRamAihEAtoRTBQlH0ECIQDN08FgTtnesgCU\n" +
+ "DFYLLcw1CiHvc7fZw4neBDHCrC8NtQIgA8TOUkGnpCZlQ0KaI8KfKWI+vxFcgFnH\n" +
+ "3fnqsTgaUs4=";
+
+ static char passphrase[] = "passphrase".toCharArray();
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile static boolean serverReady = false;
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+ SSLContext context = generateSSLContext(null, targetCertStr,
+ targetPrivateKey);
+ SSLServerSocketFactory sslssf = context.getServerSocketFactory();
+ SSLServerSocket sslServerSocket =
+ (SSLServerSocket)sslssf.createServerSocket(serverPort);
+ serverPort = sslServerSocket.getLocalPort();
+
+ /*
+ * Signal Client, we're ready for his connect.
+ */
+ serverReady = true;
+
+ try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) {
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write('A');
+ sslOS.flush();
+
+ throw new Exception(
+ "RSA keys shorter than 1024 bits should be disabled");
+ } catch (SSLHandshakeException sslhe) {
+ // the expected exception, ignore
+ }
+ }
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLContext context = generateSSLContext(trustedCertStr, null, null);
+ SSLSocketFactory sslsf = context.getSocketFactory();
+
+ try (SSLSocket sslSocket =
+ (SSLSocket)sslsf.createSocket("localhost", serverPort)) {
+
+ // only enable the target protocol
+ sslSocket.setEnabledProtocols(new String[] {enabledProtocol});
+
+ // enable a block cipher
+ sslSocket.setEnabledCipherSuites(
+ new String[] {"TLS_DHE_RSA_WITH_AES_128_CBC_SHA"});
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslOS.write('B');
+ sslOS.flush();
+ sslIS.read();
+
+ throw new Exception(
+ "RSA keys shorter than 1024 bits should be disabled");
+ } catch (SSLHandshakeException sslhe) {
+ // the expected exception, ignore
+ }
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+ private static String tmAlgorithm; // trust manager
+ private static String enabledProtocol; // the target protocol
+
+ private static void parseArguments(String[] args) {
+ tmAlgorithm = args[0];
+ enabledProtocol = args[1];
+ }
+
+ private static SSLContext generateSSLContext(String trustedCertStr,
+ String keyCertStr, String keySpecStr) throws Exception {
+
+ // generate certificate from cert string
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+ // create a key store
+ KeyStore ks = KeyStore.getInstance("JKS");
+ ks.load(null, null);
+
+ // import the trused cert
+ Certificate trusedCert = null;
+ ByteArrayInputStream is = null;
+ if (trustedCertStr != null) {
+ is = new ByteArrayInputStream(trustedCertStr.getBytes());
+ trusedCert = cf.generateCertificate(is);
+ is.close();
+
+ ks.setCertificateEntry("RSA Export Signer", trusedCert);
+ }
+
+ if (keyCertStr != null) {
+ // generate the private key.
+ PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(
+ new BASE64Decoder().decodeBuffer(keySpecStr));
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ RSAPrivateKey priKey =
+ (RSAPrivateKey)kf.generatePrivate(priKeySpec);
+
+ // generate certificate chain
+ is = new ByteArrayInputStream(keyCertStr.getBytes());
+ Certificate keyCert = cf.generateCertificate(is);
+ is.close();
+
+ Certificate[] chain = null;
+ if (trusedCert != null) {
+ chain = new Certificate[2];
+ chain[0] = keyCert;
+ chain[1] = trusedCert;
+ } else {
+ chain = new Certificate[1];
+ chain[0] = keyCert;
+ }
+
+ // import the key entry.
+ ks.setKeyEntry("Whatever", priKey, passphrase, chain);
+ }
+
+ // create SSL context
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm);
+ tmf.init(ks);
+
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ if (keyCertStr != null && !keyCertStr.isEmpty()) {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");
+ kmf.init(ks, passphrase);
+
+ ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ ks = null;
+ } else {
+ ctx.init(null, tmf.getTrustManagers(), null);
+ }
+
+ return ctx;
+ }
+
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+ if (debug)
+ System.setProperty("javax.net.debug", "all");
+
+ /*
+ * Get the customized arguments.
+ */
+ parseArguments(args);
+
+ /*
+ * Start the tests.
+ */
+ new DisabledShortRSAKeys();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ DisabledShortRSAKeys() throws Exception {
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ // swallow for now. Show later
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+ String whichRemote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ whichRemote = "server";
+ } else {
+ remote = clientException;
+ local = serverException;
+ whichRemote = "client";
+ }
+
+ /*
+ * If both failed, return the curthread's exception, but also
+ * print the remote side Exception
+ */
+ if ((local != null) && (remote != null)) {
+ System.out.println(whichRemote + " also threw:");
+ remote.printStackTrace();
+ System.out.println();
+ throw local;
+ }
+
+ if (remote != null) {
+ throw remote;
+ }
+
+ if (local != null) {
+ throw local;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died...");
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died...");
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ clientException = e;
+ }
+ }
+ }
+}
--- a/jdk/test/sun/security/ssl/javax/net/ssl/TLSv12/ShortRSAKey512.java Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/TLSv12/ShortRSAKey512.java Fri Dec 28 18:36:41 2012 -0800
@@ -23,6 +23,9 @@
* questions.
*/
+// This test case relies on updated static security property, no way to re-use
+// security property in samevm/agentvm mode.
+
/*
* @test
* @bug 7106773
@@ -38,6 +41,7 @@
import java.util.*;
import java.io.*;
import javax.net.ssl.*;
+import java.security.Security;
import java.security.KeyStore;
import java.security.KeyFactory;
import java.security.cert.Certificate;
@@ -275,6 +279,10 @@
volatile Exception clientException = null;
public static void main(String[] args) throws Exception {
+ // reset the security property to make sure that the algorithms
+ // and keys used in this test are not disabled.
+ Security.setProperty("jdk.certpath.disabledAlgorithms", "MD2");
+
if (debug)
System.setProperty("javax.net.debug", "all");
--- a/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh Fri Dec 28 18:36:41 2012 -0800
@@ -44,10 +44,10 @@
;;
esac
-# Choose 512-bit RSA to make sure it runs fine and fast on all platforms. In fact,
-# every keyalg/keysize combination is OK for this test.
+# Choose 1024-bit RSA to make sure it runs fine and fast on all platforms. In
+# fact, every keyalg/keysize combination is OK for this test.
-KT="$TESTJAVA${FS}bin${FS}keytool -storepass changeit -keypass changeit -keystore js.jks -keyalg rsa -keysize 512"
+KT="$TESTJAVA${FS}bin${FS}keytool -storepass changeit -keypass changeit -keystore js.jks -keyalg rsa -keysize 1024"
JAR=$TESTJAVA${FS}bin${FS}jar
JARSIGNER=$TESTJAVA${FS}bin${FS}jarsigner
JAVAC=$TESTJAVA${FS}bin${FS}javac
--- a/jdk/test/sun/tools/jrunscript/common.sh Fri Dec 28 18:30:56 2012 -0800
+++ b/jdk/test/sun/tools/jrunscript/common.sh Fri Dec 28 18:36:41 2012 -0800
@@ -63,8 +63,4 @@
JRUNSCRIPT="${TESTJAVA}/bin/jrunscript"
JAVAC="${TESTJAVA}/bin/javac"
JAVA="${TESTJAVA}/bin/java"
- # needed to get full headless behavior on Mac
- if [ "$OS" = "Darwin" ]; then
- export AWT_TOOLKIT=XToolkit
- fi
}