diff --git a/Jenkinsfile b/Jenkinsfile index d1891aad95e..c0a11a98e7c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -43,7 +43,7 @@ pipeline { parameters { choice(name: 'nodeLabel', choices: ['ubuntu', 's390x', 'arm', 'Windows']) - choice(name: 'jdkVersion', choices: ['jdk_17_latest', 'jdk_21_latest', 'jdk_24_latest', 'jdk_17_latest_windows', 'jdk_21_latest_windows', 'jdk_24_latest_windows']) + choice(name: 'jdkVersion', choices: ['jdk_17_latest', 'jdk_21_latest', 'jdk_25_latest', 'jdk_17_latest_windows', 'jdk_21_latest_windows', 'jdk_25_latest_windows']) booleanParam(name: 'deployEnabled', defaultValue: false) booleanParam(name: 'sonarEnabled', defaultValue: false) booleanParam(name: 'testsEnabled', defaultValue: true) @@ -72,12 +72,12 @@ pipeline { } } - stage('Build JDK 24') { + stage('Build JDK 25') { tools { - jdk "jdk_24_latest" + jdk "jdk_25_latest" } steps { - echo 'Building JDK 24' + echo 'Building JDK 25' sh 'java -version' sh 'mvn -version' sh 'mvn -U -B -e clean install -DskipTests' diff --git a/activemq-broker/pom.xml b/activemq-broker/pom.xml index 3aa264161d6..e77e1244fcf 100644 --- a/activemq-broker/pom.xml +++ b/activemq-broker/pom.xml @@ -42,6 +42,10 @@ org.apache.activemq activemq-client + + org.apache.activemq + activemq-compat + org.apache.activemq activemq-openwire-legacy diff --git a/activemq-broker/src/main/java/org/apache/activemq/broker/jmx/AnnotatedMBean.java b/activemq-broker/src/main/java/org/apache/activemq/broker/jmx/AnnotatedMBean.java index 51c135ddf56..1f6639a31b2 100644 --- a/activemq-broker/src/main/java/org/apache/activemq/broker/jmx/AnnotatedMBean.java +++ b/activemq-broker/src/main/java/org/apache/activemq/broker/jmx/AnnotatedMBean.java @@ -16,13 +16,12 @@ */ package org.apache.activemq.broker.jmx; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.Principal; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; +import org.apache.activemq.broker.util.AuditLogEntry; +import org.apache.activemq.broker.util.AuditLogService; +import org.apache.activemq.broker.util.JMXAuditLogEntry; +import org.apache.activemq.compat.sm.SecurityManagerShim; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; @@ -33,12 +32,12 @@ import javax.management.ReflectionException; import javax.management.StandardMBean; import javax.security.auth.Subject; - -import org.apache.activemq.broker.util.AuditLogEntry; -import org.apache.activemq.broker.util.AuditLogService; -import org.apache.activemq.broker.util.JMXAuditLogEntry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.security.Principal; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; /** * MBean that looks for method/parameter descriptions in the Info annotation. @@ -205,8 +204,7 @@ public Object invoke(String s, Object[] objects, String[] strings) throws MBeanE objects = (objects == null) ? new Object[]{} : objects; JMXAuditLogEntry entry = null; if (audit != OFF) { - // [AMQ-9563] TODO: JDK 21 use Subject.current() instead - Subject subject = Subject.getSubject(AccessController.getContext()); + Subject subject = SecurityManagerShim.currentSubject(); String caller = "anonymous"; if (subject != null) { caller = ""; diff --git a/activemq-client/pom.xml b/activemq-client/pom.xml index ebc8c5facf1..70cc08f6053 100644 --- a/activemq-client/pom.xml +++ b/activemq-client/pom.xml @@ -227,7 +227,7 @@ ${surefire.argLine} alphabetical - target + target/ + + + 4.0.0 + + + org.apache.activemq + activemq-parent + 6.2.1-SNAPSHOT + + + activemq-compat + bundle + ActiveMQ :: Compat + The ActiveMQ Compatibility (MRJAR) + + + + + org.apache.felix + maven-bundle-plugin + true + + + + !java.*, + !javax.naming*, + !javax.net*, + !javax.security*, + !org.apache.activemq*, + * + + true + <_noee>true + + + + + + + + + jdk24-plus + + [24,) + + + + + maven-compiler-plugin + + + java24-compile + compile + + compile + + + 24 + ${project.basedir}/src/main/java24 + true + + + + + + + + + + diff --git a/activemq-compat/src/main/java/org/apache/activemq/compat/sm/SecurityManagerShim.java b/activemq-compat/src/main/java/org/apache/activemq/compat/sm/SecurityManagerShim.java new file mode 100644 index 00000000000..f45051418b0 --- /dev/null +++ b/activemq-compat/src/main/java/org/apache/activemq/compat/sm/SecurityManagerShim.java @@ -0,0 +1,199 @@ +/* + * 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.activemq.compat.sm; + +import javax.security.auth.Subject; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; + +/* + * SecurityManager related shim. + * This specific class uses legacy methods toward usage on Java 17 - 23. + * + * The API of this class must be kept the same as the Java 24+ implementation + * variant of this class which can be found at: + * src/main/java24/org/apache/activemq/artemis/utils/sm/SecurityManagerShim.java + * + * Did not use a shared interface since the shim seems unlikely to be changed much, + * other than future removals, and doing so would need singleton instance indirection + * at every call site to access the purely static onward methods being called. + */ +@SuppressWarnings("removal") +public class SecurityManagerShim { + + /** + * Returns the current associated subject. + *

+ * On Java 17-23, retrieves the current AccessControlContext by calling + * {@code AccessController.getContext()} and then returns result of method + * {@code Subject.getSubject(accessControlContext)}. + *

+ * On Java 24+, returns result of method {@code Subject.current()}. + * + * @return the current associated subject, or null if none was found. + */ + public static Subject currentSubject() { + AccessControlContext accessControlContext = AccessController.getContext(); + if (accessControlContext != null) { + return Subject.getSubject(accessControlContext); + } + return null; + } + + /** + * Perform work as a particular {@code Subject}. + *

+ * On Java 17-23, wraps the given {@code callable} as a {@code PrivilegedExceptionAction} and executes it + * via {@code Subject.doAs(final Subject subject, final java.security.PrivilegedExceptionAction action)}. + *

+ * On Java 24+, returns result of calling {@code Subject.callAs(final Subject subject, + * final Callable action)}. + *

+ * Any exceptions thrown by the {@code callable.call()} will result in a {@code CompletionException} being + * thrown with the original exception as its cause. + * + * @param subject the {@code Subject} that the given {@code callable} will run as, may be null. + * @param callable the {@code Callable} to be run, must not be {@code null}. + * @param the type of value returned by the {@code callable}. + * @return the value returned by the {@code callable}. + * @throws NullPointerException if {@code callable} is {@code null}. + * @throws CompletionException if {@code callable.call()} throws an exception. + * The cause is set to the exception thrown by {@code callable.call()}. + */ + public static T callAs(final Subject subject, final Callable callable) throws CompletionException { + // Subject is allowed to be null + Objects.requireNonNull(callable, "callable must be provided"); + + try { + final PrivilegedExceptionAction pa = () -> callable.call(); + + return Subject.doAs(subject, pa); + } catch (PrivilegedActionException e) { + throw new CompletionException(e.getCause()); + } catch (Exception e) { + throw new CompletionException(e); + } + } + + /** + * Returns whether a SecurityManager is enabled. + *

+ * On Java 17-23, returns whether result of check: {@code System.getSecurityManager() != null}. + *

+ * On Java 24+, returns false as a SecurityManager can never be present. + * + * @return true if a SecurityManager is present, or false otherwise. + */ + public static boolean isSecurityManagerEnabled() { + return System.getSecurityManager() != null; + } + + /** + * Returns the current AccessControlContext. + *

+ * On Java 17-23, returns the result of {@code AccessController.getContext()}. + *

+ * On Java 24+, always returns null. + * + * @return the current AccessControlContext, or null if none. + */ + public static Object getAccessControlContext() { + return AccessController.getContext(); + } + + /** + * Performs the specified {@code PrivilegedAction}. + *

+ * On Java 17-23, returns the result of passing the given {@code action} to the + * {@code AccessController.doPrivileged(PrivilegedAction action)} method. + *

+ * On Java 24+, returns the result of running {@code action.run()} directly. + *

+ * If the action's {@code run} method throws an unchecked exception it will + * propagate through this method. + * + * @param action the {@code PrivilegedAction} to be run, must not be {@code null}. + * @param the type of value returned by the {@code action}. + * @return the value returned by the {@code action}. + * @throws NullPointerException if {@code action} is {@code null}. + */ + public static T doPrivileged(final PrivilegedAction action) { + Objects.requireNonNull(action, "action must be provided"); + + return AccessController.doPrivileged(action); + } + + /** + * Performs the specified {@code PrivilegedAction}. + *

+ * On Java 17-23, returns the result of calling the + * {@code AccessController.doPrivileged(PrivilegedAction action, AccessControlContext context)} + * method with the given {@code action} and {@code accessControlContext}. + *

+ * On Java 24+, returns the result of running {@code action.run()} directly, + * ignoring the accessControlContext parameter. + * + * If the action's {@code run} method throws an unchecked exception it will + * propagate through this method. + * + * @param action the {@code PrivilegedAction} to be run, must not be {@code null}. + * @param accessControlContext the {@code AccessControlContext} object, may be null. + * @param the type of value returned by the {@code action}. + * @return the value returned by the {@code action}. + * @throws NullPointerException if {@code action} is {@code null}. + */ + public static T doPrivileged(final PrivilegedAction action, final Object accessControlContext) { + // AccessControlContext may be null + Objects.requireNonNull(action, "action must be provided"); + + final AccessControlContext acc = AccessControlContext.class.cast(accessControlContext); + + return AccessController.doPrivileged(action, acc); + } + + /** + * Performs the specified {@code PrivilegedExceptionAction}. + *

+ * On Java 17-23, returns the result of calling + * {@code AccessController.doPrivileged(PrivilegedExceptionAction action)} + * with the given {@code exceptionAction}. + *

+ * On Java 24+, returns the result of running {@code exceptionAction.run()} directly. + *

+ * If the action's {@code run} method throws an unchecked exception it will + * propagate through this method. + * + * @param exceptionAction the {@code PrivilegedExceptionAction} to be run, must not be {@code null}. + * @param the type of value returned by the {@code exceptionAction}. + * @return the value returned by the {@code action}. + * @throws NullPointerException if {@code exceptionAction} is {@code null}. + * @throws PrivilegedActionException if {@code exceptionAction.run()} throws a checked exception. + * The cause is set to the exception thrown {@code callable.call()}. + */ + public static T doPrivileged(final PrivilegedExceptionAction exceptionAction) throws PrivilegedActionException { + Objects.requireNonNull(exceptionAction, "exceptionAction must be provided"); + + return AccessController.doPrivileged(exceptionAction); + } + +} \ No newline at end of file diff --git a/activemq-compat/src/main/java24/org/apache/activemq/compat/sm/SecurityManagerShim.java b/activemq-compat/src/main/java24/org/apache/activemq/compat/sm/SecurityManagerShim.java new file mode 100644 index 00000000000..6f76cb4f1bc --- /dev/null +++ b/activemq-compat/src/main/java24/org/apache/activemq/compat/sm/SecurityManagerShim.java @@ -0,0 +1,96 @@ +/* + * 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.activemq.compat.sm; + +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; + +import javax.security.auth.Subject; + +/* + * SecurityManager related shim. + * This specific class uses replacement APIs, direct-executes actions, + * or no-ops as appropriate towards usage on Java 24+. + * + * The API of this class must be kept the same as the Java 17-23 implementation + * variant of this class which can be found at: + * src/main/java/org/apache/activemq/artemis/utils/sm/SecurityManagerShim.java + * + * The javadoc there should also be kept in sync, it covers both classes behaviour. + * + * Did not use a shared interface since the shim seems unlikely to be changed much, + * other than future removals, and doing so would need singleton instance indirection + * at every call site to access the purely static onward methods being called. + */ +public class SecurityManagerShim { + + public static Subject currentSubject() { + return Subject.current(); + } + + public static T callAs(final Subject subject, final Callable callable) throws CompletionException { + // Subject is allowed to be null + Objects.requireNonNull(callable); + + return Subject.callAs(subject, callable); + } + + public static boolean isSecurityManagerEnabled() { + // Can never be enabled, it was removed in Java 24+. + return false; + } + + public static Object getAccessControlContext() { + // AccessControlContext is now only useful with a SecurityManager, + // which can never be used in Java 24+. Return null so calling + // code can determine there is nothing to be do re: SecurityManager. + return null; + } + + public static T doPrivileged(final PrivilegedAction action) { + Objects.requireNonNull(action, "action must be provided"); + + return action.run(); + } + + public static T doPrivileged(final PrivilegedAction action, final Object accessControlContext) { + // We ignore the accessControlContext parameter as it was only useful + // with a SecurityManager, which can never be used in Java 24+. + Objects.requireNonNull(action, "action must be provided"); + + return action.run(); + } + + public static T doPrivileged(final PrivilegedExceptionAction exceptionAction) throws PrivilegedActionException { + Objects.requireNonNull(exceptionAction, "exceptionAction must be provided"); + + try { + return exceptionAction.run(); + } catch (RuntimeException re) { + // RuntimeExceptions were re-thrown directly by doPrivileged, only checked + // Exceptions were wrapped in PrivilegedActionException to throw for intercept. + throw re; + } catch (Exception e) { + throw new PrivilegedActionException(e); + } + } + +} \ No newline at end of file diff --git a/activemq-console/pom.xml b/activemq-console/pom.xml index 77b8b632ada..18114c2a985 100644 --- a/activemq-console/pom.xml +++ b/activemq-console/pom.xml @@ -133,7 +133,7 @@ false true - target + target/ diff --git a/activemq-jaas/pom.xml b/activemq-jaas/pom.xml index 34f1552de34..ef60b64089d 100644 --- a/activemq-jaas/pom.xml +++ b/activemq-jaas/pom.xml @@ -40,7 +40,7 @@ -Xmx512M - target + target/ - - - + diff --git a/pom.xml b/pom.xml index 028cf65864c..d62eca07b02 100644 --- a/pom.xml +++ b/pom.xml @@ -182,6 +182,7 @@ bom activemq-openwire-generator activemq-client + activemq-compat activemq-openwire-legacy activemq-broker activemq-stomp @@ -270,6 +271,11 @@ ${project.version} + + org.apache.activemq + activemq-compat + ${project.version} + org.apache.activemq activemq-client