Skip to content

Commit

Permalink
JDO 808: Java 18 compatibility (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
tzaeschke authored Apr 28, 2022
1 parent 3fa6543 commit e623503
Show file tree
Hide file tree
Showing 13 changed files with 421 additions and 98 deletions.
41 changes: 32 additions & 9 deletions api/src/main/java/javax/jdo/JDOHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -177,7 +176,7 @@ static Map<String, String> createAttributePropertyXref() {
* implementations.
*/
private static JDOImplHelper implHelper = (JDOImplHelper)
AccessController.doPrivileged(
doPrivileged(
new PrivilegedAction<JDOImplHelper> () {
public JDOImplHelper run () {
return JDOImplHelper.getInstance();
Expand Down Expand Up @@ -1910,7 +1909,7 @@ public static JDOEnhancer getEnhancer(ClassLoader loader) {
* @since 2.0
*/
private static ClassLoader getContextClassLoader() {
return AccessController.doPrivileged(
return doPrivileged(
new PrivilegedAction<ClassLoader> () {
public ClassLoader run () {
return Thread.currentThread().getContextClassLoader();
Expand All @@ -1924,7 +1923,7 @@ public ClassLoader run () {
*/
private static InputStream getResourceAsStream(
final ClassLoader resourceLoader, final String name) {
return AccessController.doPrivileged(
return doPrivileged(
new PrivilegedAction<InputStream>() {
public InputStream run() {
return resourceLoader.getResourceAsStream(name);
Expand All @@ -1948,7 +1947,7 @@ private static Method getMethod(
final Class<?>[] parameterTypes)
throws NoSuchMethodException {
try {
return AccessController.doPrivileged(
return doPrivileged(
new PrivilegedExceptionAction<Method>() {
public Method run() throws NoSuchMethodException {
return implClass.getMethod(methodName, parameterTypes);
Expand All @@ -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<Object>() {
public Object run()
throws IllegalAccessException,
Expand Down Expand Up @@ -1998,7 +1997,7 @@ protected static Enumeration<URL> getResources(
final String resourceName)
throws IOException {
try {
return AccessController.doPrivileged(
return doPrivileged(
new PrivilegedExceptionAction<Enumeration<URL>>() {
public Enumeration<URL> run() throws IOException {
return resourceLoader.getResources(resourceName);
Expand All @@ -2024,7 +2023,7 @@ private static Class<?> forName(
final ClassLoader loader)
throws ClassNotFoundException {
try {
return AccessController.doPrivileged(
return doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
return Class.forName(name, init, loader);
Expand All @@ -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<InputStream>() {
public InputStream run() throws IOException {
return url.openStream();
Expand All @@ -2057,4 +2056,28 @@ public InputStream run() throws IOException {
}
}

@SuppressWarnings("unchecked")
private static <T> T doPrivileged(PrivilegedAction<T> 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> T doPrivileged(PrivilegedExceptionAction<T> 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);
}
}
}
186 changes: 186 additions & 0 deletions api/src/main/java/javax/jdo/LegacyJava.java
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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 <T> Return type.
* @return Return value of the action.
*/
public static <T> T doPrivileged(PrivilegedAction<T> 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 <T> Return type.
* @return Return value of the action.
* @throws PrivilegedActionException Never thrown.
*/
public static <T> T doPrivileged(PrivilegedExceptionAction<T> privilegedAction)
throws PrivilegedActionException {
try {
return privilegedAction.run();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new PrivilegedActionException(e);
}
}
}
20 changes: 17 additions & 3 deletions api/src/main/java/javax/jdo/identity/ObjectIdentity.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<JDOImplHelper> () {
public JDOImplHelper run () {
return JDOImplHelper.getInstance();
}
}
);


@SuppressWarnings("unchecked")
private static <T> T doPrivileged(PrivilegedAction<T> 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
Expand Down
19 changes: 16 additions & 3 deletions api/src/main/java/javax/jdo/spi/I18NHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<ClassLoader> () {
public ClassLoader run () {
return cls.getClassLoader();
Expand Down Expand Up @@ -389,12 +390,24 @@ final private static String getPackageName(final String className)
* block because of security.
*/
private static ClassLoader getSystemClassLoaderPrivileged() {
return AccessController.doPrivileged (
return doPrivileged (
new PrivilegedAction<ClassLoader> () {
public ClassLoader run () {
return ClassLoader.getSystemClassLoader();
}
}
);
}

@SuppressWarnings("unchecked")
private static <T> T doPrivileged(PrivilegedAction<T> 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());
}
}
}
Loading

0 comments on commit e623503

Please sign in to comment.