From e62350343be9ccc00e256dfb15793693de968267 Mon Sep 17 00:00:00 2001 From: Tilmann Date: Thu, 28 Apr 2022 21:40:51 +0200 Subject: [PATCH] JDO 808: Java 18 compatibility (#41) --- api/src/main/java/javax/jdo/JDOHelper.java | 41 +++- api/src/main/java/javax/jdo/LegacyJava.java | 186 ++++++++++++++++++ .../javax/jdo/identity/ObjectIdentity.java | 20 +- .../main/java/javax/jdo/spi/I18NHelper.java | 19 +- .../java/javax/jdo/spi/JDOImplHelper.java | 40 ++-- .../jdo/identity/ObjectIdentityTest.java | 19 +- .../test/java/javax/jdo/util/XMLTestUtil.java | 20 +- tck/RunRules.md | 86 ++++---- .../apache/jdo/tck/AbstractReaderTest.java | 18 +- .../java/org/apache/jdo/tck/JDO_Test.java | 29 +-- .../api/persistencemanagerfactory/Close.java | 18 +- ...houtPermissionThrowsSecurityException.java | 4 + .../apache/jdo/tck/util/JDOJdk14Logger.java | 19 +- 13 files changed, 421 insertions(+), 98 deletions(-) create mode 100644 api/src/main/java/javax/jdo/LegacyJava.java diff --git a/api/src/main/java/javax/jdo/JDOHelper.java b/api/src/main/java/javax/jdo/JDOHelper.java index 69c84617b..aa465064d 100644 --- a/api/src/main/java/javax/jdo/JDOHelper.java +++ b/api/src/main/java/javax/jdo/JDOHelper.java @@ -32,7 +32,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; -import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; @@ -177,7 +176,7 @@ static Map createAttributePropertyXref() { * implementations. */ private static JDOImplHelper implHelper = (JDOImplHelper) - AccessController.doPrivileged( + doPrivileged( new PrivilegedAction () { public JDOImplHelper run () { return JDOImplHelper.getInstance(); @@ -1910,7 +1909,7 @@ public static JDOEnhancer getEnhancer(ClassLoader loader) { * @since 2.0 */ private static ClassLoader getContextClassLoader() { - return AccessController.doPrivileged( + return doPrivileged( new PrivilegedAction () { public ClassLoader run () { return Thread.currentThread().getContextClassLoader(); @@ -1924,7 +1923,7 @@ public ClassLoader run () { */ private static InputStream getResourceAsStream( final ClassLoader resourceLoader, final String name) { - return AccessController.doPrivileged( + return doPrivileged( new PrivilegedAction() { public InputStream run() { return resourceLoader.getResourceAsStream(name); @@ -1948,7 +1947,7 @@ private static Method getMethod( final Class[] parameterTypes) throws NoSuchMethodException { try { - return AccessController.doPrivileged( + return doPrivileged( new PrivilegedExceptionAction() { public Method run() throws NoSuchMethodException { return implClass.getMethod(methodName, parameterTypes); @@ -1967,7 +1966,7 @@ private static Object invoke(final Method method, final Object instance, final Object[] parameters) throws IllegalAccessException, InvocationTargetException { try { - return (Object) AccessController.doPrivileged( + return (Object) doPrivileged( new PrivilegedExceptionAction() { public Object run() throws IllegalAccessException, @@ -1998,7 +1997,7 @@ protected static Enumeration getResources( final String resourceName) throws IOException { try { - return AccessController.doPrivileged( + return doPrivileged( new PrivilegedExceptionAction>() { public Enumeration run() throws IOException { return resourceLoader.getResources(resourceName); @@ -2024,7 +2023,7 @@ private static Class forName( final ClassLoader loader) throws ClassNotFoundException { try { - return AccessController.doPrivileged( + return doPrivileged( new PrivilegedExceptionAction>() { public Class run() throws ClassNotFoundException { return Class.forName(name, init, loader); @@ -2045,7 +2044,7 @@ public Class run() throws ClassNotFoundException { private static InputStream openStream(final URL url) throws IOException { try { - return AccessController.doPrivileged( + return doPrivileged( new PrivilegedExceptionAction() { public InputStream run() throws IOException { return url.openStream(); @@ -2057,4 +2056,28 @@ public InputStream run() throws IOException { } } + @SuppressWarnings("unchecked") + private static T doPrivileged(PrivilegedAction privilegedAction) { + try { + return (T) LegacyJava.doPrivilegedAction.invoke(null, privilegedAction); + } catch (IllegalAccessException | InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new JDOFatalInternalException(e.getMessage()); + } + } + + @SuppressWarnings("unchecked") + private static T doPrivileged(PrivilegedExceptionAction privilegedAction) + throws PrivilegedActionException { + try { + return (T) LegacyJava.doPrivilegedExceptionAction.invoke(null, privilegedAction); + } catch (IllegalAccessException | InvocationTargetException e) { + if (e.getCause() instanceof PrivilegedActionException) { + throw (PrivilegedActionException) e.getCause(); + } + throw new PrivilegedActionException(e); + } + } } diff --git a/api/src/main/java/javax/jdo/LegacyJava.java b/api/src/main/java/javax/jdo/LegacyJava.java new file mode 100644 index 000000000..5a1ddab53 --- /dev/null +++ b/api/src/main/java/javax/jdo/LegacyJava.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package javax.jdo; + +import javax.jdo.spi.JDOPermission; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.*; + +/** + * This class provides wrapper implementations for the Java SecurityManager and AccessController classes + * that were deprecated in Java 17/18. + *

+ * For pre-17 this class provides access to the expected SecurityManager and AccessController. + * For 17+ the getSecurityManager() will always return 'null' and the doPrivileged() methods will simply execute + * the lambda argument without any security checks. + *

+ * Solution for AccessControl: This class uses reflection to detect whether AccessControl is deprecated. + * If it is deprecated we provide dummy implementation of 'doPrivileged'. If it is not deprecated we use the + * original implementation. + * Implementations are used by finding the desired methods and store them in a static field in LegacyJava. + * Whenever we need to call a method we can use reflection again to call the stored method in LegacyJava. + *

+ * Please note that the call to the stored method needs to happen from the original calling class. We cannot provide + * such a generic doPrivileged() in LegacyJava because it would be accessible from other classes and thus + * allow *any* caller to execute *any* code with doPrivileged() with the security context of LegacyJava. For the same + * reason the doPrivileged() implementations in the calling classes must be *private*. + */ +public class LegacyJava { + + private static final boolean isSecurityDeprecated = initIsSecurityDeprecated(); + private static final Method getSecurityManager = isSecurityDeprecated ? null : + findMethod("java.lang.System", "getSecurityManager"); + private static final SecurityManager securityManager = getSecurityManager == null ? null : new SecurityManager(); + public static final Method doPrivilegedAction = + findMethod("java.security.AccessController", "doPrivileged", LegacyJava.class.getName(), PrivilegedAction.class); + public static final Method doPrivilegedExceptionAction = + findMethod("java.security.AccessController", "doPrivileged", LegacyJava.class.getName(), PrivilegedExceptionAction.class); + + /** + * @return A wrapper around the java SecurityManager or 'null' if no SecurityManager is available or if it + * is deprecated (Java 17 and later). + */ + public static SecurityManager getSecurityManager() { + if (getSecurityManager == null) { + return null; + } + Object sm; + try { + sm = getSecurityManager.invoke(null); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + throw new JDOFatalInternalException(e.getMessage()); + } + if (sm == null) { + return null; + } + + securityManager.updateSecurityManager(sm); + return securityManager; + } + + /** + * @return 'true' if the security manager is deprecated or does not exist. + */ + public static boolean isSecurityManagerDeprecated() { + return isSecurityDeprecated; + } + + /** + * The SecurityManager is only instantiated if it is not deprecated/removed in the Java API. + * It wraps the java SecurityManager and will forward any calls to it. + * It is updated in every call to getSecurityManager() to ensure that it uses the latest Java SecurityManager + * instance. + */ + public static class SecurityManager { + Object sm = null; + Method checkPermissionMethod = null; + + public void checkPermission(JDOPermission permission) { + try { + checkPermissionMethod.invoke(null, permission); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + throw new JDOFatalInternalException(e.getMessage()); + } + } + + public void updateSecurityManager(Object sm) { + if (this.sm != sm) { + // We have a new security manager! + this.sm = sm; + if (sm != null) { + checkPermissionMethod = + findMethod("java.lang.SecurityManager", "checkPermission", Permission.class); + } else { + checkPermissionMethod = null; + } + } + } + } + + /** + * @return 'true' if the security manager is deprecated or does not exist. + */ + private static boolean initIsSecurityDeprecated() { + try { + Method getSecurityManager = System.class.getMethod("getSecurityManager"); + return getSecurityManager.isAnnotationPresent(Deprecated.class); + } catch (NoSuchMethodException e) { + return true; + } + } + + private static Method findMethod(String cName, String mName, Class... args) { + if (isSecurityDeprecated) { + return null; + } + try { + Class accessController = Class.forName(cName); + return accessController.getMethod(mName, args); + } catch (NoSuchMethodException | ClassNotFoundException e) { + throw new JDOFatalInternalException(e.getMessage()); + } + } + + private static Method findMethod(String cName, String mName, String cNameAlt, Class... args) { + if (isSecurityDeprecated) { + cName = cNameAlt; + } + try { + Class accessController = Class.forName(cName); + return accessController.getMethod(mName, args); + } catch (NoSuchMethodException | ClassNotFoundException e) { + throw new JDOFatalInternalException(e.getMessage()); + } + } + + /** + * This is a replacement for the old Java AccessControl.doPrivileged(). This replacement is used when the + * old doPrivileged() is deprecated or not available. + * This replacement simply execute the action without any further checks. + * + * @param privilegedAction The action to execute. + * @param Return type. + * @return Return value of the action. + */ + public static T doPrivileged(PrivilegedAction privilegedAction) { + return privilegedAction.run(); + } + + /** + * This is a replacement for the old Java AccessControl.doPrivileged(). This replacement is used when the + * old doPrivileged() is deprecated or not available. + * This replacement simply execute the action without any further checks. + * + * @param privilegedAction The action to execute. + * @param Return type. + * @return Return value of the action. + * @throws PrivilegedActionException Never thrown. + */ + public static T doPrivileged(PrivilegedExceptionAction privilegedAction) + throws PrivilegedActionException { + try { + return privilegedAction.run(); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new PrivilegedActionException(e); + } + } +} diff --git a/api/src/main/java/javax/jdo/identity/ObjectIdentity.java b/api/src/main/java/javax/jdo/identity/ObjectIdentity.java index 2526f559a..751d22001 100644 --- a/api/src/main/java/javax/jdo/identity/ObjectIdentity.java +++ b/api/src/main/java/javax/jdo/identity/ObjectIdentity.java @@ -26,7 +26,9 @@ import java.io.ObjectInput; import java.io.ObjectOutput; -import java.security.AccessController; +import javax.jdo.JDOFatalInternalException; +import javax.jdo.LegacyJava; +import java.lang.reflect.InvocationTargetException; import java.security.PrivilegedAction; import javax.jdo.JDOUserException; @@ -44,14 +46,26 @@ public class ObjectIdentity extends SingleFieldIdentity { /** The JDOImplHelper instance used for parsing the String to an Object. */ private static JDOImplHelper helper = (JDOImplHelper) - AccessController.doPrivileged( + doPrivileged( new PrivilegedAction () { public JDOImplHelper run () { return JDOImplHelper.getInstance(); } } ); - + + @SuppressWarnings("unchecked") + private static T doPrivileged(PrivilegedAction privilegedAction) { + try { + return (T) LegacyJava.doPrivilegedAction.invoke(null, privilegedAction); + } catch (IllegalAccessException | InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new JDOFatalInternalException(e.getMessage()); + } + } + /** The delimiter for String constructor. */ private static final String STRING_DELIMITER = ":"; //NOI18N diff --git a/api/src/main/java/javax/jdo/spi/I18NHelper.java b/api/src/main/java/javax/jdo/spi/I18NHelper.java index 1f9687d59..4e3abaae1 100644 --- a/api/src/main/java/javax/jdo/spi/I18NHelper.java +++ b/api/src/main/java/javax/jdo/spi/I18NHelper.java @@ -17,9 +17,10 @@ package javax.jdo.spi; +import java.lang.reflect.InvocationTargetException; import java.util.*; import java.text.MessageFormat; -import java.security.AccessController; +import javax.jdo.LegacyJava; import java.security.PrivilegedAction; import javax.jdo.JDOFatalInternalException; @@ -107,7 +108,7 @@ public static I18NHelper getInstance (String bundleName) { * @return the helper instance bound to the bundle */ public static I18NHelper getInstance (final Class cls) { - ClassLoader classLoader = AccessController.doPrivileged ( + ClassLoader classLoader = doPrivileged ( new PrivilegedAction () { public ClassLoader run () { return cls.getClassLoader(); @@ -389,7 +390,7 @@ final private static String getPackageName(final String className) * block because of security. */ private static ClassLoader getSystemClassLoaderPrivileged() { - return AccessController.doPrivileged ( + return doPrivileged ( new PrivilegedAction () { public ClassLoader run () { return ClassLoader.getSystemClassLoader(); @@ -397,4 +398,16 @@ public ClassLoader run () { } ); } + + @SuppressWarnings("unchecked") + private static T doPrivileged(PrivilegedAction privilegedAction) { + try { + return (T) LegacyJava.doPrivilegedAction.invoke(null, privilegedAction); + } catch (IllegalAccessException | InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new JDOFatalInternalException(e.getMessage()); + } + } } diff --git a/api/src/main/java/javax/jdo/spi/JDOImplHelper.java b/api/src/main/java/javax/jdo/spi/JDOImplHelper.java index 85a20b2e8..4436161ac 100644 --- a/api/src/main/java/javax/jdo/spi/JDOImplHelper.java +++ b/api/src/main/java/javax/jdo/spi/JDOImplHelper.java @@ -24,9 +24,12 @@ import org.xml.sax.ErrorHandler; +import javax.jdo.LegacyJava; +import javax.jdo.LegacyJava.SecurityManager; + import java.lang.reflect.Constructor; -import java.security.AccessController; +import java.lang.reflect.InvocationTargetException; import java.security.PrivilegedAction; import java.text.DateFormat; @@ -47,11 +50,7 @@ import java.util.Set; import java.util.WeakHashMap; -import javax.jdo.Constants; -import javax.jdo.JDOException; -import javax.jdo.JDOFatalInternalException; -import javax.jdo.JDOFatalUserException; -import javax.jdo.JDOUserException; +import javax.jdo.*; import javax.xml.parsers.DocumentBuilderFactory; /** This class is a helper class for JDO implementations. It contains methods @@ -186,8 +185,7 @@ static Set createUserConfigurableStandardPropertiesLowerCased() { } return Collections.unmodifiableSet(lowerCased); } - - + /** Register the default DateFormat instance. */ static { @@ -208,7 +206,7 @@ private JDOImplHelper() { */ public static JDOImplHelper getInstance() throws SecurityException { - SecurityManager sec = System.getSecurityManager(); + SecurityManager sec = LegacyJava.getSecurityManager(); if (sec != null) { // throws exception if caller is not authorized sec.checkPermission (JDOPermission.GET_METADATA); @@ -454,7 +452,7 @@ public static void registerClass (Class pcClass, */ public void unregisterClasses (ClassLoader cl) { - SecurityManager sec = System.getSecurityManager(); + SecurityManager sec = LegacyJava.getSecurityManager(); if (sec != null) { // throws exception if caller is not authorized sec.checkPermission (JDOPermission.MANAGE_METADATA); @@ -491,7 +489,7 @@ public void unregisterClass (Class pcClass) { if (pcClass == null) throw new NullPointerException(msg.msg("ERR_NullClass")); //NOI18N - SecurityManager sec = System.getSecurityManager(); + SecurityManager sec = LegacyJava.getSecurityManager(); if (sec != null) { // throws exception if caller is not authorized sec.checkPermission (JDOPermission.MANAGE_METADATA); @@ -575,7 +573,7 @@ public static void registerAuthorizedStateManagerClass (Class smClass) throws SecurityException { if (smClass == null) throw new NullPointerException(msg.msg("ERR_NullClass")); //NOI18N - SecurityManager sm = System.getSecurityManager(); + SecurityManager sm = LegacyJava.getSecurityManager(); if (sm != null) { sm.checkPermission(JDOPermission.SET_STATE_MANAGER); } @@ -597,7 +595,7 @@ public static void registerAuthorizedStateManagerClass (Class smClass) */ public static void registerAuthorizedStateManagerClasses ( Collection smClasses) throws SecurityException { - SecurityManager sm = System.getSecurityManager(); + SecurityManager sm = LegacyJava.getSecurityManager(); if (sm != null) { sm.checkPermission(JDOPermission.SET_STATE_MANAGER); synchronized (authorizedStateManagerClasses) { @@ -680,7 +678,7 @@ public static void checkAuthorizedStateManager (StateManager sm) { * @since 1.0.1 */ public static void checkAuthorizedStateManagerClass (Class smClass) { - final SecurityManager scm = System.getSecurityManager(); + final SecurityManager scm = LegacyJava.getSecurityManager(); if (scm == null) { // if no security manager, no checking. return; @@ -879,7 +877,7 @@ public static Object construct(String className, String keyString) { static DateFormat getDateTimeInstance() { DateFormat result = null; try { - result = AccessController.doPrivileged ( + result = doPrivileged ( new PrivilegedAction () { public DateFormat run () { return DateFormat.getDateTimeInstance(); @@ -892,6 +890,18 @@ public DateFormat run () { return result; } + @SuppressWarnings("unchecked") + private static T doPrivileged(PrivilegedAction privilegedAction) { + try { + return (T) LegacyJava.doPrivilegedAction.invoke(null, privilegedAction); + } catch (IllegalAccessException | InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new JDOFatalInternalException(e.getMessage()); + } + } + /** * Register a DateFormat instance for use with constructing Date * instances. The default is the default DateFormat instance. diff --git a/api/src/test/java/javax/jdo/identity/ObjectIdentityTest.java b/api/src/test/java/javax/jdo/identity/ObjectIdentityTest.java index 8264400c2..af957726a 100644 --- a/api/src/test/java/javax/jdo/identity/ObjectIdentityTest.java +++ b/api/src/test/java/javax/jdo/identity/ObjectIdentityTest.java @@ -27,7 +27,8 @@ import java.math.BigDecimal; -import java.security.AccessController; +import javax.jdo.JDOFatalInternalException; +import javax.jdo.LegacyJava; import java.security.PrivilegedAction; import java.text.SimpleDateFormat; @@ -52,14 +53,26 @@ public class ObjectIdentityTest extends SingleFieldIdentityTest { /** The JDOImplHelper instance used for Date formatting. */ private static JDOImplHelper helper = (JDOImplHelper) - AccessController.doPrivileged( + doPrivileged( new PrivilegedAction () { public JDOImplHelper run () { return JDOImplHelper.getInstance(); } } ); - + + @SuppressWarnings("unchecked") + private static T doPrivileged(PrivilegedAction privilegedAction) { + try { + return (T) LegacyJava.doPrivilegedAction.invoke(null, privilegedAction); + } catch (IllegalAccessException | InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new JDOFatalInternalException(e.getMessage()); + } + } + /** Creates a new instance of ObjectIdentityTest */ public ObjectIdentityTest() { } diff --git a/api/src/test/java/javax/jdo/util/XMLTestUtil.java b/api/src/test/java/javax/jdo/util/XMLTestUtil.java index 7a5473721..10681a7ac 100644 --- a/api/src/test/java/javax/jdo/util/XMLTestUtil.java +++ b/api/src/test/java/javax/jdo/util/XMLTestUtil.java @@ -26,7 +26,9 @@ import java.io.FileFilter; import java.io.FilenameFilter; -import java.security.AccessController; +import javax.jdo.JDOFatalInternalException; +import javax.jdo.LegacyJava; +import java.lang.reflect.InvocationTargetException; import java.security.PrivilegedAction; import java.util.Arrays; import java.util.ArrayList; @@ -416,7 +418,7 @@ public InputSource resolveEntity(String publicId, final String systemId) // but only if the publicId is equal to RECOGNIZED_PUBLIC_ID // or there is no publicID and the systemID is equal to // RECOGNIZED_SYSTEM_ID. - InputStream stream = AccessController.doPrivileged ( + InputStream stream = doPrivileged ( new PrivilegedAction () { public InputStream run () { return getClass().getClassLoader(). @@ -436,6 +438,7 @@ public InputStream run () { } } + /** Helper class to find all test JDO metadata files. */ public static class XMLFinder { @@ -564,5 +567,18 @@ public static void main(String args[]) { System.out.println(messages); } } + + + @SuppressWarnings("unchecked") + private static T doPrivileged(PrivilegedAction privilegedAction) { + try { + return (T) LegacyJava.doPrivilegedAction.invoke(null, privilegedAction); + } catch (IllegalAccessException | InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new JDOFatalInternalException(e.getMessage()); + } + } } diff --git a/tck/RunRules.md b/tck/RunRules.md index 4a08808e1..2c59a6586 100644 --- a/tck/RunRules.md +++ b/tck/RunRules.md @@ -35,11 +35,11 @@ Overview ----------------------- In order to demonstrate compliance with the Java Data Objects -specification, an implementation must successfully run all of the TCK -tests that are not on the \"excluded\" list. The implementation is +specification, an implementation must successfully run all TCK +tests that are not on the "excluded" list. The implementation is hereinafter referred to as the IUT (Implementation Under Test). -The results must be posted on a publicly accessible web site for +The results must be posted on a publicly accessible website for examination by the public. The posting includes the output of the test run, which consists of multiple log files containing configuration information and test results. For an example of the required posting, @@ -50,7 +50,7 @@ please see . Prerequisites ----------------------- -In order to run the TCK, you must install Maven 2.x Maven +In order to run the TCK, you must install Maven 3.x Maven is the driver of the test programs. You must test the IUT on all configurations that the IUT supports. This @@ -62,65 +62,65 @@ Java, and different datastores. The TCK supports Java version 1.8. Installation ----------------------- -Download the jdo.3.x-src.zip or jdo.3.x-src.gz file from the +Download the `jdo.3.x-src.zip` or `jdo.3.x-src.gz` file from the distribution location. Unpack the archive file into a directory of your -choice. Follow the instructions in the root level README.html file for +choice. Follow the instructions in the root level [README.md](../README.md) file for building and running the JDO TCK. Please take note of the following files: -- Maven configuration file \"pom.xml\" in the top level directory and +- Maven configuration file `pom.xml` in the top level directory and subprojects. These files must not be changed with the exception of the one in tck (see below). -- lib/ext - Copy the jar files fscontext.jar and providerutil.jar used +- `lib/ext` - Copy the jar files `fscontext.jar` and `providerutil.jar` used by the JNDI tests into this directory. It is permitted to use a - different JNDI implementation; see README.html for more information. + different JNDI implementation; see [README.md](../README.md) for more information. - the TCK directory, which contains: - - Maven configuration file \"pom.xml\". Please edit the file and + - Maven configuration file `pom.xml`. Please edit the file and add the iut dependencies to the profile called iut. - - assertions - contains the assertions file identifying the + - `assertions` - contains the assertions file identifying the assertions tested by the tests. This is for reference. - - target - this directory contains artifacts of compiling and + - `target` - this directory contains artifacts of compiling and running the tests. It does not exist in the distribution and will be created by the maven build process. - - src - this directory contains the test configuration files and + - `src` - this directory contains the test configuration files and directories: - - testdata - this directory contains data (represented as .xml + - `testdata` - this directory contains data (represented as .xml files) loaded into the datastore for tests. These files must not be modified. - - sql - this directory contains DDL to define the tables used + - `sql` - this directory contains DDL to define the tables used in the tests. The files distributed must not be modified. Files may be created for databases for which the DDL for the database under test is not provided. - - jdo - this directory contains .jdo metadata files for the + - `jdo` - this directory contains .jdo metadata files for the persistent classes used in the tests. These files must not be modified. - - orm - this directory contains .orm metadata files to map the + - `orm` - this directory contains .orm metadata files to map the persistent classes to the sql tables. These files must not be modified except to add DDL-generation information (which is not used by the TCK). - - java - this directory contains the source code to the TCK + - `java` - this directory contains the source code to the TCK tests. These files must not be modified. - - conf - this directory contains the configuration information + - `conf` - this directory contains the configuration information for the test runs. The files iut-pmf.properties, - iut-log4j.properties, iut-jdoconfig.xml, and - iut-persistence.xml in this directory must be changed to + `iut-log4j.properties`, `iut-jdoconfig.xml`, and + `iut-persistence.xml` in this directory must be changed to provide properties for the IUT persistence manager factory. - The file jndi.properties may be changed to use a different + The file `jndi.properties` may be changed to use a different jndi provider. Other files must not be modified, except to put a successfully challenged test case into the - trunk/tck/test/conf/exclude.list. Please see below. + `trunk/tck/test/conf/exclude.list`. Please see below. @@ -131,18 +131,18 @@ The Implementation Under Test (IUT) can be installed into the lib/iut directory. Any jar files in this directory are added to the class path used to run the tests. -There are properties in the build.properties file in the TCK directory +There are properties in the `build.properties` file in the TCK directory that must be changed to configure the IUT execution and enhancement -(optional) environment. These properties begin with iut.runtck and -iut.enhancer. +(optional) environment. These properties begin with `iut.runtck` and +`iut.enhancer`. There is are three properties files that must be modified to be -IUT-specific, all located in the TCK src/conf directory. The -iut-pmf.properties file contains information used to construct the -PersistenceManagerFactory used in the tests. iut-jdoconfig.xml and -iut-persistence.xml also contain PersistenceManagerFactory properties +IUT-specific, all located in the TCK `src/conf` directory. The +`iut-pmf.properties` file contains information used to construct the +`PersistenceManagerFactory` used in the tests. `iut-jdoconfig.xml` and +`iut-persistence.xml` also contain `PersistenceManagerFactory` properties used only in tests in the -org.apache.jdo.tck.api.persistencemanagerfactory.config package. +`org.apache.jdo.tck.api.persistencemanagerfactory.config` package. SQL DDL files are provided for the sql table definitions. The existing files must not be changed, but files may be added in the directory in @@ -154,28 +154,28 @@ implementation under test. Running the Tests ----------------------- -Follow the instructions in README.html for running the TCK on the JDO +Follow the instructions in [README.md](../README.md) for running the TCK on the JDO Reference Implementation and the Implementation Under Test. This will -produce console output plus a directory in the tck/target/logs directory +produce console output plus a directory in the `tck/target/logs` directory whose name contains the date/time the tests were started. This directory contains the output of the tests. This is the directory to be published. -Some of the TCK tests require the implementation to support up to 20 -instances of PersistenceManager with open transactions simultaneously. +Some TCK tests require the implementation to support up to 20 +instances of `PersistenceManager` with open transactions simultaneously. Debugging the IUT while Running TCK tests ----------------------- -Execute \"mvn jdo-exectck:help\" in the TCK directory in order to get +Execute `mvn jdo-exectck:help` in the TCK directory in order to get information on running the TCK tests with a debugger. In particular, -properties jdo.tck.cleanupaftertest, jdo.tck.cfglist, -jdo.tck.identitytypes, and jdo.tck.dblist may be useful. +properties `jdo.tck.cleanupaftertest`, `jdo.tck.cfglist`, +`jdo.tck.identitytypes`, and `jdo.tck.dblist` may be useful. If you make a change to the IUT enhancer while debugging the TCK tests (for implementations that use an enhancer) you must remove the -target/classes directory before continuing in order to make sure that +`target/classes` directory before continuing in order to make sure that the classes are re-enhanced by the changed code. @@ -184,7 +184,7 @@ Publishing the Results of the TCK Tests ----------------------- With a successful test run, the log directory with the results of the -tests must be published on a publicly-available web site. The unmodified +tests must be published on a publicly-available website. The unmodified directory is the self-certification of the successful TCK test run. @@ -196,8 +196,8 @@ If any test does not pass on the JDO implementation under test, this may be due to an error in the implementation or in the TCK test. If you believe that the failure is due to an error in the TCK test, you may challenge the test. To do so, send email to: -with a subject line containing \"CHALLENGE\" and the name of the test -program, e.g. org.apache.jdo.tck.api.persistencemanager.ThreadSafe.java; +with a subject line containing `CHALLENGE` and the name of the test +program, e.g. `org.apache.jdo.tck.api.persistencemanager.ThreadSafe.java;` and the body of the email containing the details of the challenge. The Maintenance Lead will respond within 15 working days with a decision @@ -207,7 +207,7 @@ Maintenance Lead might patch the erroneous test or add the test to the exclude list. The user can obtain the TCK updates by checking out the latest minor version branch, 3.n.1. If a fix is not provided within 15 working days of the receipt of the challenge, then the user may put the -test into the TCK file src/conf/exclude.list and it will not be run as +test into the TCK file `src/conf/exclude.list` and it will not be run as part of the TCK. Decisions of the Maintenance Lead may be appealed to the full expert diff --git a/tck/src/main/java/org/apache/jdo/tck/AbstractReaderTest.java b/tck/src/main/java/org/apache/jdo/tck/AbstractReaderTest.java index 9e030c668..17b371e9a 100644 --- a/tck/src/main/java/org/apache/jdo/tck/AbstractReaderTest.java +++ b/tck/src/main/java/org/apache/jdo/tck/AbstractReaderTest.java @@ -17,7 +17,9 @@ package org.apache.jdo.tck; -import java.security.AccessController; +import javax.jdo.JDOFatalInternalException; +import javax.jdo.LegacyJava; +import java.lang.reflect.InvocationTargetException; import java.security.PrivilegedAction; import java.util.HashMap; @@ -50,7 +52,7 @@ public class AbstractReaderTest extends JDO_Test { * @return the named object */ protected Object getBean(final DefaultListableBeanFactory factory, final String name) { - return AccessController.doPrivileged( + return doPrivileged( new PrivilegedAction() { public Object run() { return factory.getBean(name); @@ -59,6 +61,18 @@ public Object run() { ); } + @SuppressWarnings("unchecked") + private static T doPrivileged(PrivilegedAction privilegedAction) { + try { + return (T) LegacyJava.doPrivilegedAction.invoke(null, privilegedAction); + } catch (IllegalAccessException | InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new JDOFatalInternalException(e.getMessage()); + } + } + /** Get the root object from the bean factory. * * @param factory the bean factory diff --git a/tck/src/main/java/org/apache/jdo/tck/JDO_Test.java b/tck/src/main/java/org/apache/jdo/tck/JDO_Test.java index c8269e61b..5278c7300 100644 --- a/tck/src/main/java/org/apache/jdo/tck/JDO_Test.java +++ b/tck/src/main/java/org/apache/jdo/tck/JDO_Test.java @@ -27,9 +27,10 @@ import java.io.PrintStream; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; -import java.security.AccessController; +import javax.jdo.*; import java.security.PrivilegedAction; import java.util.ArrayList; @@ -43,16 +44,6 @@ import java.util.StringTokenizer; import java.util.Vector; -import javax.jdo.Constants; -import javax.jdo.Extent; -import javax.jdo.JDOException; -import javax.jdo.JDOFatalException; -import javax.jdo.JDOHelper; -import javax.jdo.JDOObjectNotFoundException; -import javax.jdo.PersistenceManager; -import javax.jdo.PersistenceManagerFactory; -import javax.jdo.Query; - import junit.framework.AssertionFailedError; import junit.framework.TestCase; @@ -653,7 +644,7 @@ public static void closePMF() { public static void closePMF(final PersistenceManagerFactory PMF) { if (PMF != null) { if (!PMF.isClosed()) { - AccessController.doPrivileged( + doPrivileged( new PrivilegedAction () { public Object run () { PMF.close(); @@ -665,6 +656,18 @@ public Object run () { } } + @SuppressWarnings("unchecked") + private static T doPrivileged(PrivilegedAction privilegedAction) { + try { + return (T) LegacyJava.doPrivilegedAction.invoke(null, privilegedAction); + } catch (IllegalAccessException | InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new JDOFatalInternalException(e.getMessage()); + } + } + /** * Returns failed PersistenceManagers * @param assertionFailure failure @@ -1188,7 +1191,7 @@ protected boolean mangleObject (Object oid) * @return an array of fields */ protected Field[] getModifiableFields(final Object obj) { - return (Field[])AccessController.doPrivileged( + return (Field[])doPrivileged( new PrivilegedAction () { public Object run () { Class cls = obj.getClass(); diff --git a/tck/src/main/java/org/apache/jdo/tck/api/persistencemanagerfactory/Close.java b/tck/src/main/java/org/apache/jdo/tck/api/persistencemanagerfactory/Close.java index 3a82291a3..15b503fe1 100644 --- a/tck/src/main/java/org/apache/jdo/tck/api/persistencemanagerfactory/Close.java +++ b/tck/src/main/java/org/apache/jdo/tck/api/persistencemanagerfactory/Close.java @@ -17,7 +17,9 @@ package org.apache.jdo.tck.api.persistencemanagerfactory; -import java.security.AccessController; +import javax.jdo.JDOFatalInternalException; +import javax.jdo.LegacyJava; +import java.lang.reflect.InvocationTargetException; import java.security.PrivilegedAction; import javax.jdo.JDOFatalUserException; @@ -55,6 +57,18 @@ public static void main(String[] args) { BatchTestRunner.run(Close.class); } + @SuppressWarnings("unchecked") + private static T doPrivileged(PrivilegedAction privilegedAction) { + try { + return (T) LegacyJava.doPrivilegedAction.invoke(null, privilegedAction); + } catch (IllegalAccessException | InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new JDOFatalInternalException(e.getMessage()); + } + } + /** */ public void test() { pmf = getPMF(); @@ -85,7 +99,7 @@ public void test() { // pmf.close() on already-closed pmf should not throw an exception try { // don't use closePMF methods because they check isClosed before calling - AccessController.doPrivileged( + doPrivileged( new PrivilegedAction () { public Object run () { pmf.close(); diff --git a/tck/src/main/java/org/apache/jdo/tck/api/persistencemanagerfactory/CloseWithoutPermissionThrowsSecurityException.java b/tck/src/main/java/org/apache/jdo/tck/api/persistencemanagerfactory/CloseWithoutPermissionThrowsSecurityException.java index ffcd8077c..e24eeafb0 100644 --- a/tck/src/main/java/org/apache/jdo/tck/api/persistencemanagerfactory/CloseWithoutPermissionThrowsSecurityException.java +++ b/tck/src/main/java/org/apache/jdo/tck/api/persistencemanagerfactory/CloseWithoutPermissionThrowsSecurityException.java @@ -20,6 +20,7 @@ import java.security.Permission; import javax.jdo.JDOException; +import javax.jdo.LegacyJava; import javax.jdo.PersistenceManagerFactory; import javax.jdo.spi.JDOPermission; @@ -68,6 +69,9 @@ public void test() { } private void closeWithMySecurityManager(PersistenceManagerFactory pmf) { + if (LegacyJava.isSecurityManagerDeprecated()) { + return; + } SecurityManager oldSecMgr = System.getSecurityManager(); try { System.setSecurityManager(new MySecurityManager()); diff --git a/tck/src/main/java/org/apache/jdo/tck/util/JDOJdk14Logger.java b/tck/src/main/java/org/apache/jdo/tck/util/JDOJdk14Logger.java index f3d1360c3..bb1b1700c 100644 --- a/tck/src/main/java/org/apache/jdo/tck/util/JDOJdk14Logger.java +++ b/tck/src/main/java/org/apache/jdo/tck/util/JDOJdk14Logger.java @@ -20,7 +20,9 @@ import java.io.InputStream; import java.io.IOException; -import java.security.AccessController; +import javax.jdo.JDOFatalInternalException; +import javax.jdo.LegacyJava; +import java.lang.reflect.InvocationTargetException; import java.security.PrivilegedAction; import java.util.logging.LogManager; @@ -67,7 +69,7 @@ public JDOJdk14Logger(String name) { private void configureJDK14Logger() { final LogManager logManager = LogManager.getLogManager(); final ClassLoader cl = getClass().getClassLoader(); - AccessController.doPrivileged(new PrivilegedAction() { + doPrivileged(new PrivilegedAction() { public Object run () { try { InputStream config = cl.getResourceAsStream(PROPERIES_FILE); @@ -91,5 +93,16 @@ public Object run () { } }); } - + + @SuppressWarnings("unchecked") + private static T doPrivileged(PrivilegedAction privilegedAction) { + try { + return (T) LegacyJava.doPrivilegedAction.invoke(null, privilegedAction); + } catch (IllegalAccessException | InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new JDOFatalInternalException(e.getMessage()); + } + } }