This is an automated email from the ASF dual-hosted git repository.

rzo1 pushed a commit to branch jdk24
in repository https://gitbox.apache.org/repos/asf/tomee.git

commit 23e9a782e18107215e7e1696a0e47e5456a86c20
Author: Richard Zowalla <r...@apache.org>
AuthorDate: Tue Jun 24 10:37:06 2025 +0200

    TOMEE-4474 - TomEE does not launch on Java 24
    
    Support Java 24 at Runtime at the cost of loosing EJB Method Security, 
which does not work due to the deprecation / removal of SecurityManager in Java 
21+ anyway.
---
 .../core/security/AbstractSecurityService.java     | 80 +++++++++++++-------
 .../apache/openejb/core/security/PolicyJDK24.java  | 46 +++++++++++
 .../core/security/jacc/BasicJaccProvider.java      |  3 +-
 .../openejb/client/JaasIdentityResolver.java       |  5 +-
 .../apache/openejb/client/util/JDK24Subject.java   | 88 ++++++++++++++++++++++
 .../java/org/apache/openejb/client/MainTest.java   |  5 +-
 6 files changed, 195 insertions(+), 32 deletions(-)

diff --git 
a/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
 
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
index cb56a5f9b7..97cf01ab79 100644
--- 
a/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
+++ 
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java
@@ -17,6 +17,12 @@
 
 package org.apache.openejb.core.security;
 
+import jakarta.security.jacc.EJBMethodPermission;
+import jakarta.security.jacc.PolicyConfigurationFactory;
+import jakarta.security.jacc.PolicyContext;
+import jakarta.security.jacc.PolicyContextException;
+import jakarta.security.jacc.PolicyContextHandler;
+import jakarta.servlet.http.HttpServletRequest;
 import org.apache.openejb.BeanContext;
 import org.apache.openejb.InterfaceType;
 import org.apache.openejb.api.resource.DestroyableResource;
@@ -34,12 +40,6 @@ import org.apache.openejb.util.Logger;
 
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginException;
-import jakarta.security.jacc.EJBMethodPermission;
-import jakarta.security.jacc.PolicyConfigurationFactory;
-import jakarta.security.jacc.PolicyContext;
-import jakarta.security.jacc.PolicyContextException;
-import jakarta.security.jacc.PolicyContextHandler;
-import jakarta.servlet.http.HttpServletRequest;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.security.AccessControlContext;
@@ -371,24 +371,31 @@ public abstract class AbstractSecurityService implements 
DestroyableResource, Se
 
     @Override
     public boolean isCallerAuthorized(final Method method, final InterfaceType 
type) {
-        final ThreadContext threadContext = ThreadContext.getThreadContext();
-        final BeanContext beanContext = threadContext.getBeanContext();
-        try {
-            final String ejbName = beanContext.getEjbName();
-            String name = type == null ? null : type.getSpecName();
-            if ("LocalBean".equals(name) || "LocalBeanHome".equals(name)) {
-                name = null;
-            }
-            final Identity currentIdentity = clientIdentity.get();
-            final SecurityContext securityContext;
-            if (currentIdentity == null) {
-                securityContext = threadContext.get(SecurityContext.class);
-            } else {
-                securityContext = new 
SecurityContext(currentIdentity.getSubject());
+        if 
(System.getProperty("java.vm.specification.version").compareTo("21") < 0) {
+            final ThreadContext threadContext = 
ThreadContext.getThreadContext();
+            final BeanContext beanContext = threadContext.getBeanContext();
+            try {
+
+                final String ejbName = beanContext.getEjbName();
+                String name = type == null ? null : type.getSpecName();
+                if ("LocalBean".equals(name) || "LocalBeanHome".equals(name)) {
+                    name = null;
+                }
+
+                final Identity currentIdentity = clientIdentity.get();
+                final SecurityContext securityContext;
+                if (currentIdentity == null) {
+                    securityContext = threadContext.get(SecurityContext.class);
+                } else {
+                    securityContext = new 
SecurityContext(currentIdentity.getSubject());
+                }
+
+                securityContext.getAccessControlContext().checkPermission(new 
EJBMethodPermission(ejbName, name, method));
+            } catch (final AccessControlException e) {
+                return false;
             }
-            securityContext.getAccessControlContext().checkPermission(new 
EJBMethodPermission(ejbName, name, method));
-        } catch (final AccessControlException e) {
-            return false;
+        } else {
+            LOGGER.warning("Skipping JACC authorization check for method {} on 
type {} as TomEE running on JDK 21+ does not support method security at the 
moment.", method, type);
         }
         return true;
     }
@@ -422,11 +429,11 @@ public abstract class AbstractSecurityService implements 
DestroyableResource, Se
 
         // check the system provided provider first - if for some reason it 
isn't loaded, load it
         final String systemPolicyProvider = 
SystemInstance.get().getOptions().getProperties().getProperty("jakarta.security.jacc.policy.provider");
-        if (systemPolicyProvider != null && Policy.getPolicy() == null) {
+        if (systemPolicyProvider != null && getPolicy() == null) {
             installPolicy(systemPolicyProvider);
         }
 
-        if (! 
JaccProvider.Policy.class.getName().equals(Policy.getPolicy().getClass().getName()))
 {
+        if (! 
JaccProvider.Policy.class.getName().equals(getPolicy().getClass().getName())) {
             // this should delegate to the policy installed above
             installPolicy(JaccProvider.Policy.class.getName());
         }
@@ -436,15 +443,34 @@ public abstract class AbstractSecurityService implements 
DestroyableResource, Se
         try {
             final ClassLoader classLoader = 
Thread.currentThread().getContextClassLoader();
             final Class policyClass = Class.forName(policyProvider, true, 
classLoader);
-            final Policy policy = (Policy) policyClass.newInstance();
+            final Policy policy = (Policy) 
policyClass.getDeclaredConstructor().newInstance();
             policy.refresh();
-            Policy.setPolicy(policy);
+            setPolicy(policy);
         } catch (final Exception e) {
             throw new IllegalStateException("Could not install JACC Policy 
Provider: " + policyProvider, e);
         }
     }
 
 
+    public static Policy getPolicy() {
+        Policy policy = PolicyJDK24.getPolicy();
+        if (policy == null) {
+            policy = Policy.getPolicy();
+        }
+
+        return policy;
+    }
+
+    public static void setPolicy(Policy policy) {
+        try {
+            Policy.setPolicy(policy);
+        } catch (UnsupportedOperationException e) {
+            //we are running JDK 24 or later, so no system-wide policy 
possible.
+            PolicyJDK24.setPolicy(policy);
+        }
+    }
+
+
     protected Subject createSubject(final String name, final String groupName) 
{
         if (name == null) {
             return null;
diff --git 
a/container/openejb-core/src/main/java/org/apache/openejb/core/security/PolicyJDK24.java
 
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/PolicyJDK24.java
new file mode 100644
index 0000000000..6db9ccf4cb
--- /dev/null
+++ 
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/PolicyJDK24.java
@@ -0,0 +1,46 @@
+/*
+ * 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 org.apache.openejb.core.security;
+
+import java.security.Policy;
+
+/**
+ * A utility class to manage the Java Security Policy in a thread-safe manner.
+ * This class provides methods to get and set the security policy, ensuring
+ * that changes are synchronized across threads.
+ */
+public class PolicyJDK24 {
+
+    private static volatile Policy policy;
+
+    /**
+     * @return the policy
+     */
+    public static synchronized Policy getPolicy() {
+        return policy;
+    }
+
+    /**
+     * @param policy the policy to set
+     */
+    public static synchronized void setPolicy(Policy policy) {
+        PolicyJDK24.policy = policy;
+    }
+
+
+
+}
diff --git 
a/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java
 
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java
index 3db5c4af63..6a2f77811b 100644
--- 
a/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java
+++ 
b/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java
@@ -18,6 +18,7 @@
 package org.apache.openejb.core.security.jacc;
 
 import org.apache.openejb.core.security.JaccProvider;
+import org.apache.openejb.core.security.PolicyJDK24;
 import org.apache.openejb.loader.SystemInstance;
 import org.apache.openejb.spi.SecurityService;
 
@@ -66,7 +67,7 @@ public class BasicJaccProvider extends JaccProvider {
     private final java.security.Policy systemPolicy;
 
     public BasicJaccProvider() {
-        systemPolicy = Policy.getPolicy();
+        systemPolicy = PolicyJDK24.getPolicy();
     }
 
     public PolicyConfiguration getPolicyConfiguration(final String contextID, 
final boolean remove) throws PolicyContextException {
diff --git 
a/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java
 
b/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java
index 2193d03aaa..26fcf5cfd4 100644
--- 
a/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java
+++ 
b/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java
@@ -17,15 +17,16 @@
  */
 package org.apache.openejb.client;
 
+import org.apache.openejb.client.util.JDK24Subject;
+
 import javax.security.auth.Subject;
-import java.security.AccessController;
 import java.util.Set;
 
 public class JaasIdentityResolver implements IdentityResolver {
 
     @Override
     public Object getIdentity() {
-        final Subject subject = 
Subject.getSubject(AccessController.getContext());
+        final Subject subject = JDK24Subject.currentSubject();
         if (subject == null) {
             return null;
         }
diff --git 
a/server/openejb-client/src/main/java/org/apache/openejb/client/util/JDK24Subject.java
 
b/server/openejb-client/src/main/java/org/apache/openejb/client/util/JDK24Subject.java
new file mode 100644
index 0000000000..3b35ff8d87
--- /dev/null
+++ 
b/server/openejb-client/src/main/java/org/apache/openejb/client/util/JDK24Subject.java
@@ -0,0 +1,88 @@
+/*
+ * 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 org.apache.openejb.client.util;
+
+import javax.security.auth.Subject;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class JDK24Subject {
+
+    private static final MethodHandle CURRENT = lookupCurrent();
+
+    /**
+     * Maps to Subject.current() is available, otherwise maps to 
Subject.getSubject()
+     * @return the current subject
+     */
+    public static Subject currentSubject() {
+        try {
+            return (Subject) CURRENT.invoke();
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+    }
+
+    private static MethodHandle lookupCurrent() {
+        final MethodHandles.Lookup lookup = MethodHandles.lookup();
+        try {
+            // Subject.getSubject(AccessControlContext) is deprecated for 
removal and replaced by
+            // Subject.current().
+            // Lookup first the new API, since for Java versions where both 
exists, the
+            // new API delegates to the old API (for example Java 18, 19 and 
20).
+            // Otherwise (Java 17), lookup the old API.
+            return lookup.findStatic(Subject.class, "current",
+                    MethodType.methodType(Subject.class));
+        } catch (NoSuchMethodException e) {
+            final MethodHandle getContext = lookupGetContext();
+            final MethodHandle getSubject = lookupGetSubject();
+            return MethodHandles.filterReturnValue(getContext, getSubject);
+        } catch (IllegalAccessException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    private static MethodHandle lookupGetSubject() {
+        final MethodHandles.Lookup lookup = MethodHandles.lookup();
+        try {
+            final Class<?> contextClazz =
+                    ClassLoader.getSystemClassLoader()
+                            .loadClass("java.security.AccessControlContext");
+            return lookup.findStatic(Subject.class, "getSubject",
+                    MethodType.methodType(Subject.class, contextClazz));
+        } catch (ClassNotFoundException | NoSuchMethodException | 
IllegalAccessException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    private static MethodHandle lookupGetContext() {
+        try {
+            // Use reflection to work with Java versions that have and don't 
have AccessController.
+            final Class<?> controllerClazz =
+                    
ClassLoader.getSystemClassLoader().loadClass("java.security.AccessController");
+            final Class<?> contextClazz =
+                    ClassLoader.getSystemClassLoader()
+                            .loadClass("java.security.AccessControlContext");
+
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+            return lookup.findStatic(controllerClazz, "getContext",
+                    MethodType.methodType(contextClazz));
+        } catch (ClassNotFoundException | NoSuchMethodException | 
IllegalAccessException e) {
+            throw new AssertionError(e);
+        }
+    }
+}
diff --git 
a/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java 
b/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java
index ea10f2e667..380407a481 100644
--- 
a/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java
+++ 
b/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java
@@ -18,6 +18,7 @@
 package org.apache.openejb.client;
 
 import junit.framework.TestCase;
+import org.apache.openejb.client.util.JDK24Subject;
 
 import javax.naming.Binding;
 import javax.naming.Context;
@@ -87,7 +88,7 @@ public class MainTest extends TestCase {
     public static class SecureMain {
 
         public static void main(String[] args) {
-            Subject subject = 
Subject.getSubject(AccessController.getContext());
+            Subject subject = JDK24Subject.currentSubject();
 
             // verify subject
             assertEquals("Should have one principal", 1, 
subject.getPrincipals().size());
@@ -109,7 +110,7 @@ public class MainTest extends TestCase {
     public static class NormalMain {
 
         public static void main(String[] args) {
-            Subject subject = 
Subject.getSubject(AccessController.getContext());
+            Subject subject = JDK24Subject.currentSubject();
 
             assertNull("subject is not null", subject);
 

Reply via email to