From 49586341a84d5d06b9d3fc6bee596877f74ed7be Mon Sep 17 00:00:00 2001 From: Kalin Chan <73829904+kalinchan@users.noreply.github.com> Date: Mon, 17 Jun 2024 12:24:45 +0100 Subject: [PATCH] Merge pull request #6677 from flowlogix/fix-leaks FISH-8672 bugfix: removed class loader leaks --- .gitignore | 3 +- .../main/java/com/sun/ejb/EjbInvocation.java | 17 +-- .../com/sun/ejb/EjbInvocationFactory.java | 6 +- .../com/sun/ejb/containers/BaseContainer.java | 4 +- .../containers/EjbAsyncInvocationManager.java | 3 +- .../com/sun/ejb/containers/EjbAsyncTask.java | 3 +- .../ejb/containers/EjbEndpointFacadeImpl.java | 3 +- .../ejb/mdb/MessageBeanContainer.java | 6 +- ...OpenTelemetryApplicationEventListener.java | 8 +- .../tracing/jaxrs/OpenTracingHelper.java | 4 +- .../tracing/jaxrs/ResourceCache.java | 11 +- .../entitybean/container/EntityContainer.java | 3 +- .../context/PolicyContextHandlerData.java | 13 +-- .../classloaderdata/InstanceResource.java | 37 +------ .../context/JavaEEContextUtilImpl.java | 4 +- .../web/loader/CachingReflectionUtil.java | 101 ++++++++++++++++++ .../web/loader/WebappClassLoader.java | 90 +++++++++++++++- .../core/ApplicationFilterConfig.java | 5 +- .../apache/catalina/core/ContainerBase.java | 70 ++++++++---- .../apache/catalina/core/StandardEngine.java | 2 +- .../web/WebComponentInvocation.java | 6 +- core/core-parent/pom.xml | 2 + .../enterprise/loader/ASURLClassLoader.java | 42 ++++---- .../api/invocation/ComponentInvocation.java | 32 +++--- .../admin/DeployCommandSupplementalInfo.java | 8 +- 25 files changed, 346 insertions(+), 137 deletions(-) create mode 100644 appserver/web/war-util/src/main/java/org/glassfish/web/loader/CachingReflectionUtil.java diff --git a/.gitignore b/.gitignore index 39fa8643b80..cd33363f8cc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ target/ .vscode/ .idea .DS_Store +.sdkmanrc gfbuild.log nb-configuration.xml /appserver/tests/quicklook/classes/ @@ -26,4 +27,4 @@ appserver/tests/quicklook/quicklook_summary.txt **/nbproject .flattened-pom.xml **/__pycache__ -appserver/packager/legal/src/main/resources/glassfish/legal/3RD-PARTY-LICENSE.txt \ No newline at end of file +appserver/packager/legal/src/main/resources/glassfish/legal/3RD-PARTY-LICENSE.txt diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbInvocation.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbInvocation.java index 1f55e463a7d..a8c1c931cfd 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbInvocation.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbInvocation.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2019-2021] Payara Foundation and/or affiliates +// Portions Copyright [2019-2024] Payara Foundation and/or affiliates package com.sun.ejb; @@ -269,7 +269,7 @@ public class EjbInvocation // EjbInvocation(String compEnvId, Container container) { super.componentId = compEnvId; - super.container = container; + setContainer(container); super.setComponentInvocationType(ComponentInvocation.ComponentInvocationType.EJB_INVOCATION); EjbBundleDescriptor ejbBundleDesc = container.getEjbDescriptor().getEjbBundleDescriptor(); @@ -371,6 +371,7 @@ public EjbInvocation clone() { @Override public Object getJaccEjb() { Object bean = null; + Object container = getContainer(); if( container != null ) { bean = ((Container) container).getJaccEjb(this); } @@ -452,7 +453,7 @@ public void setTransactionOperationsManager(TransactionOperationsManager transac */ @Override public boolean userTransactionMethodsAllowed() { - return ((Container) container).userTransactionMethodsAllowed(this); + return ((Container) getContainer()).userTransactionMethodsAllowed(this); } /** @@ -461,7 +462,7 @@ public boolean userTransactionMethodsAllowed() { */ @Override public void userTransactionLookupAllowed() throws NameNotFoundException { - ((BaseContainer) container).checkUserTransactionLookup(this); + ((BaseContainer) getContainer()).checkUserTransactionLookup(this); } /** @@ -469,7 +470,7 @@ public void userTransactionLookupAllowed() throws NameNotFoundException { */ @Override public void doAfterUtxBegin() { - ((Container) container).doAfterBegin(this); + ((Container) getContainer()).doAfterBegin(this); } public InterceptorManager.InterceptorChain getInterceptorChain() { @@ -650,11 +651,11 @@ public Object[] getInterceptorInstances() { @Override public Object invokeBeanMethod() throws Throwable { - return ((BaseContainer) container).invokeBeanMethod(this); + return ((BaseContainer) getContainer()).invokeBeanMethod(this); } public com.sun.enterprise.security.SecurityManager getEjbSecurityManager() { - return ((BaseContainer)container).getSecurityManager(); + return ((BaseContainer) getContainer()).getSecurityManager(); } @Override @@ -684,7 +685,7 @@ public boolean authorizeWebService(Method m) throws Exception { private Exception authorizeWebServiceAndSetMethod(Method m) { try { this.method = m; - if (((com.sun.ejb.Container) container).authorize(this)) { + if (((com.sun.ejb.Container) getContainer()).authorize(this)) { // Record the method on which the successful // authorization check was performed. setWebServiceMethod(m); diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbInvocationFactory.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbInvocationFactory.java index 8864e94ee60..30890c279d4 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbInvocationFactory.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbInvocationFactory.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2018-2020] [Payara Foundation and/or its affiliates] +// Portions Copyright [2018-2024] [Payara Foundation and/or its affiliates] package com.sun.ejb; @@ -58,7 +58,7 @@ public EjbInvocationFactory(String compEnvId, Container container) { public EjbInvocation create() { EjbInvocation ejbInv = new EjbInvocation(compEnvId, container); - ejbInv.jndiEnvironment = container.getEjbDescriptor(); + ejbInv.setJNDIEnvironment(container.getEjbDescriptor()); return ejbInv; } @@ -67,7 +67,7 @@ public EjbInvocation create(Object ejb, C ctx) { ejbInv.ejb = ejb; ejbInv.instance = ejb; ejbInv.context = ctx; - ejbInv.jndiEnvironment = container.getEjbDescriptor(); + ejbInv.setJNDIEnvironment(container.getEjbDescriptor()); return ejbInv; } diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java index 693490e7a21..5d3c51edc92 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2016-2021] [Payara Foundation and/or its affiliates] +// Portions Copyright [2016-2024] [Payara Foundation and/or its affiliates] package com.sun.ejb.containers; @@ -2002,7 +2002,7 @@ public void preInvoke(EjbInvocation inv) { } inv.transactionAttribute = inv.invocationInfo.txAttr; - inv.container = this; + inv.setContainer(this); if (inv.mustInvokeAsynchronously()) { return; diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbAsyncInvocationManager.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbAsyncInvocationManager.java index 2f65813fdb7..34d6367bb38 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbAsyncInvocationManager.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbAsyncInvocationManager.java @@ -37,6 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2016-2024] [Payara Foundation and/or its affiliates] package com.sun.ejb.containers; @@ -158,7 +159,7 @@ public void cleanupContainerTasks(Container container) { Map.Entry next = iterator.next(); EjbAsyncTask task = next.getValue().getEjbAsyncTask(); - if( task.getEjbInvocation().container == container ) { + if( task.getEjbInvocation().getContainer() == container ) { removedTasks.add(task.getInvId()); diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbAsyncTask.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbAsyncTask.java index 670fc6f5b39..04a629091f4 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbAsyncTask.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbAsyncTask.java @@ -37,6 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2019-2024] Payara Foundation and/or affiliates package com.sun.ejb.containers; @@ -88,7 +89,7 @@ EjbInvocation getEjbInvocation() { public V call() throws Exception { V returnValue = null; - BaseContainer container = (BaseContainer) inv.container; + BaseContainer container = (BaseContainer) inv.getContainer(); ClassLoader prevCL = Thread.currentThread().getContextClassLoader(); try { Utility.setContextClassLoader(container.getClassLoader()); diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbEndpointFacadeImpl.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbEndpointFacadeImpl.java index f6f7e81a043..f4601ef4ca8 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbEndpointFacadeImpl.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EjbEndpointFacadeImpl.java @@ -37,6 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2019-2024] Payara Foundation and/or affiliates package com.sun.ejb.containers; @@ -89,7 +90,7 @@ public ComponentInvocation startInvocation() { // Do the portions of preInvoke that don't need a Method object. inv.isWebService = true; - inv.container = container_; + inv.setContainer(container_); inv.transactionAttribute = Container.TX_NOT_INITIALIZED; // AS per latest spec change, the MessageContext object in WebSvcCtxt diff --git a/appserver/ejb/ejb-full-container/src/main/java/org/glassfish/ejb/mdb/MessageBeanContainer.java b/appserver/ejb/ejb-full-container/src/main/java/org/glassfish/ejb/mdb/MessageBeanContainer.java index 37ee49d0365..90c511003ad 100644 --- a/appserver/ejb/ejb-full-container/src/main/java/org/glassfish/ejb/mdb/MessageBeanContainer.java +++ b/appserver/ejb/ejb-full-container/src/main/java/org/glassfish/ejb/mdb/MessageBeanContainer.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2017-2021] [Payara Foundation and/or its affiliates] +// Portions Copyright [2017-2024] [Payara Foundation and/or its affiliates] package org.glassfish.ejb.mdb; import com.sun.appserv.connectors.internal.api.ConnectorRuntime; @@ -216,7 +216,7 @@ public final class MessageBeanContainer extends BaseContainer implements Message messageBeanClient_ = clientFactory.createMessageBeanClient(msgBeanDesc); componentInvocation = createComponentInvocation(); - componentInvocation.container = this; + componentInvocation.setContainer(this); invocationManager.preInvoke(componentInvocation); messageBeanClient_.setup(this); @@ -1135,7 +1135,7 @@ public void beforeMessageDelivery(Method method, MessageDeliveryType deliveryTyp invocation.context = context; invocation.instance = context.getEJB(); invocation.ejb = context.getEJB(); - invocation.container = this; + invocation.setContainer(this); // Message Bean Container only starts a new transaction if // there is no imported transaction and the message listener diff --git a/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/OpenTelemetryApplicationEventListener.java b/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/OpenTelemetryApplicationEventListener.java index f8fe1d7565e..45b8daccacd 100644 --- a/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/OpenTelemetryApplicationEventListener.java +++ b/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/OpenTelemetryApplicationEventListener.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) [2023] Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) [2023-2024] Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -79,6 +79,12 @@ public void postConstruct() { @Override public void onEvent(final ApplicationEvent event) { + switch (event.getType()) { + case DESTROY_FINISHED: + case RELOAD_FINISHED: + openTracingHelper.canTraceCache.clear(event.getResourceConfig().getClassLoader()); + break; + } LOG.config(() -> "onEvent(event.type=" + event.getType() + ")"); } diff --git a/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/OpenTracingHelper.java b/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/OpenTracingHelper.java index ffbd756bdc4..da047811b48 100644 --- a/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/OpenTracingHelper.java +++ b/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/OpenTracingHelper.java @@ -2,7 +2,7 @@ * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2023 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2023-2024 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -368,7 +368,7 @@ private Traced computeTracedAnnotation(ResourceInfo resourceInfo, BeanManager be return result == null ? NULL_TRACED : result; } - private static ResourceCache canTraceCache = new ResourceCache<>(); + static ResourceCache canTraceCache = new ResourceCache<>(); /** * Helper method that checks if any specified skip patterns match this method name * diff --git a/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/ResourceCache.java b/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/ResourceCache.java index e3684f9c2b6..c81d6457590 100644 --- a/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/ResourceCache.java +++ b/appserver/payara-appserver-modules/microprofile/telemetry/src/main/java/fish/payara/microprofile/telemetry/tracing/jaxrs/ResourceCache.java @@ -2,7 +2,7 @@ * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2023 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2023-2024 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -47,7 +47,6 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; -import org.eclipse.microprofile.opentracing.Traced; class ResourceCache { @@ -83,4 +82,12 @@ public int hashCode() { T get(ResourceInfo info, Supplier supplier) { return tracedCache.computeIfAbsent(new ResourceKey(info), k -> supplier.get()); } + + /** + * clear all classes belonging to this class loader + * @param classLoader + */ + void clear(ClassLoader classLoader) { + tracedCache.entrySet().removeIf(entry -> entry.getKey().resourceClass.getClassLoader() == classLoader); + } } diff --git a/appserver/persistence/entitybean-container/src/main/java/org/glassfish/persistence/ejb/entitybean/container/EntityContainer.java b/appserver/persistence/entitybean-container/src/main/java/org/glassfish/persistence/ejb/entitybean/container/EntityContainer.java index 56ece86c7ee..caf56d83822 100644 --- a/appserver/persistence/entitybean-container/src/main/java/org/glassfish/persistence/ejb/entitybean/container/EntityContainer.java +++ b/appserver/persistence/entitybean-container/src/main/java/org/glassfish/persistence/ejb/entitybean/container/EntityContainer.java @@ -37,6 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2019-2024] Payara Foundation and/or affiliates package org.glassfish.persistence.ejb.entitybean.container; @@ -1284,7 +1285,7 @@ private void internalRemoveBeanUnchecked(EJBLocalRemoteObject localRemoteObj, bo context.incrementCalls(); inv.instance = inv.ejb = context.getEJB(); - inv.container = this; + inv.setContainer(this); invocationManager.preInvoke(inv); // call ejbLoad if necessary diff --git a/appserver/security/core-ee/src/main/java/com/sun/enterprise/security/jacc/context/PolicyContextHandlerData.java b/appserver/security/core-ee/src/main/java/com/sun/enterprise/security/jacc/context/PolicyContextHandlerData.java index e79587931ce..fe7c524621a 100644 --- a/appserver/security/core-ee/src/main/java/com/sun/enterprise/security/jacc/context/PolicyContextHandlerData.java +++ b/appserver/security/core-ee/src/main/java/com/sun/enterprise/security/jacc/context/PolicyContextHandlerData.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2018-2021] [Payara Foundation and/or its affiliates] +// Portions Copyright [2018-2024] [Payara Foundation and/or its affiliates] package com.sun.enterprise.security.jacc.context; import static com.sun.enterprise.security.jacc.context.PolicyContextHandlerImpl.EJB_ARGUMENTS; @@ -54,6 +54,7 @@ import org.glassfish.api.invocation.ComponentInvocation; import org.glassfish.internal.api.Globals; +import java.lang.ref.WeakReference; /** * This class implements thread scoped data used for the JACC PolicyContext. @@ -69,7 +70,7 @@ public class PolicyContextHandlerData { private HttpServletRequest httpServletRequest; - private ComponentInvocation invocation; + private WeakReference invocation; private PolicyContextDelegate ejbDelegate; private PolicyContextHandlerData() { @@ -85,7 +86,7 @@ public void setHttpServletRequest(HttpServletRequest httpReq) { } public void setInvocation(ComponentInvocation inv) { - this.invocation = inv; + this.invocation = new WeakReference<>(inv); } public Object get(String key) { @@ -107,15 +108,15 @@ public Object get(String key) { } if (SOAP_MESSAGE.equalsIgnoreCase(key)) { - return ejbDelegate != null ? ejbDelegate.getSOAPMessage(invocation) : null; + return ejbDelegate != null ? ejbDelegate.getSOAPMessage(invocation.get()) : null; } if (ENTERPRISE_BEAN.equalsIgnoreCase(key)) { - return ejbDelegate != null ? ejbDelegate.getEnterpriseBean(invocation) : null; + return ejbDelegate != null ? ejbDelegate.getEnterpriseBean(invocation.get()) : null; } if (EJB_ARGUMENTS.equalsIgnoreCase(key)) { - return ejbDelegate != null ? ejbDelegate.getEJbArguments(invocation) : null; + return ejbDelegate != null ? ejbDelegate.getEJbArguments(invocation.get()) : null; } return null; diff --git a/appserver/tests/payara-samples/classloader-data-api/src/main/java/fish/payara/samples/classloaderdata/InstanceResource.java b/appserver/tests/payara-samples/classloader-data-api/src/main/java/fish/payara/samples/classloaderdata/InstanceResource.java index 786a9f71d47..ffa162a3789 100644 --- a/appserver/tests/payara-samples/classloader-data-api/src/main/java/fish/payara/samples/classloaderdata/InstanceResource.java +++ b/appserver/tests/payara-samples/classloader-data-api/src/main/java/fish/payara/samples/classloaderdata/InstanceResource.java @@ -46,12 +46,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.LockSupport; import java.util.stream.Collectors; -import jakarta.el.BeanELResolver; -import jakarta.el.ELContext; -import jakarta.el.ELResolver; -import jakarta.el.FunctionMapper; -import jakarta.el.PropertyNotFoundException; -import jakarta.el.VariableMapper; import jakarta.enterprise.context.ApplicationScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @@ -100,11 +94,9 @@ public String getEarLibClassLoaderCount(@PathParam("timeout") Long timeout) { private static String instanceGetter(Class clazz, AtomicInteger previousValue, Long _timeout) { long timeout = Optional.ofNullable(_timeout).orElse(4L) / 2L; int previous = previousValue.updateAndGet(prev -> prev < 0 ? InstanceCounter.getInstanceCount(clazz, timeout) : prev); - forceSoftReferenceCleanup(); + memoryPressure(); System.gc(); LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(timeout)); - forceELResolverCleanup(); - System.gc(); previousValue.set(InstanceCounter.getInstanceCount(clazz, timeout)); return String.format("Instances Before GC: %d\nInstances Remaining: %d\nInstances: \n%s\n", previous, previousValue.get(), InstanceCounter.getInstances(clazz, timeout).stream() @@ -112,36 +104,11 @@ private static String instanceGetter(Class clazz, AtomicInteger previousValue .collect(Collectors.joining("\n\n"))); } - private static void forceSoftReferenceCleanup() { + private static void memoryPressure() { try { Object[] ignored = new Object[(int) Runtime.getRuntime().maxMemory()]; } catch (OutOfMemoryError e) { // Ignore } } - - private static void forceELResolverCleanup() { - BeanELResolver resolver = new BeanELResolver(); - try { - // has a sideffect of cleaning up the soft references from the map - resolver.getType(new ELContext() { - @Override - public ELResolver getELResolver() { - return resolver; - } - - @Override - public FunctionMapper getFunctionMapper() { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public VariableMapper getVariableMapper() { - throw new UnsupportedOperationException("Not supported"); - } - }, new Object(), new Object()); - } catch (PropertyNotFoundException ex) { - // do nothing - } - } } diff --git a/appserver/web/gf-web-connector/src/main/java/fish/payara/appserver/context/JavaEEContextUtilImpl.java b/appserver/web/gf-web-connector/src/main/java/fish/payara/appserver/context/JavaEEContextUtilImpl.java index c70880f3fcd..6be744cc1cf 100644 --- a/appserver/web/gf-web-connector/src/main/java/fish/payara/appserver/context/JavaEEContextUtilImpl.java +++ b/appserver/web/gf-web-connector/src/main/java/fish/payara/appserver/context/JavaEEContextUtilImpl.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) [2016-2021] Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) [2016-2024] Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -214,7 +214,7 @@ private InstanceImpl(ComponentInvocation currentInvocation) { } else if (currentInvocation.getJNDIEnvironment() instanceof JndiNameEnvironment) { componentId = DOLUtils.toEarComponentId( DOLUtils.getApplicationName((JndiNameEnvironment) - currentInvocation.jndiEnvironment)); + currentInvocation.getJNDIEnvironment())); isApplicationComponent = true; } else { // checkState() later should error out due to this condition diff --git a/appserver/web/war-util/src/main/java/org/glassfish/web/loader/CachingReflectionUtil.java b/appserver/web/war-util/src/main/java/org/glassfish/web/loader/CachingReflectionUtil.java new file mode 100644 index 00000000000..c2bc0ce40af --- /dev/null +++ b/appserver/web/war-util/src/main/java/org/glassfish/web/loader/CachingReflectionUtil.java @@ -0,0 +1,101 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2024] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package org.glassfish.web.loader; + + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class CachingReflectionUtil { + private static final Logger logger = LogFacade.getLogger(); + private static final Map> classCache = new ConcurrentHashMap<>(); + private static final Map methodCache = new ConcurrentHashMap<>(); + private static final Map fieldCache = new ConcurrentHashMap<>(); + + public static Class getClassFromCache(String className, ClassLoader classLoader) { + var cls = classCache.computeIfAbsent(className, k -> { + try { + return classLoader.loadClass(className); + } catch (ClassNotFoundException e) { + logger.log(Level.FINE, "Class not found: " + className, e); + return null; + } + }); + return cls; + } + + public static Method getMethodFromCache(Class cls, String methodName, boolean isPrivate, Class... parameterTypes) { + return methodCache.computeIfAbsent(methodName, k -> { + try { + if (isPrivate) { + Method method = cls.getDeclaredMethod(methodName, parameterTypes); + method.setAccessible(true); + return method; + } else { + return cls.getMethod(methodName, parameterTypes); + } + } catch (NoSuchMethodException e) { + logger.log(Level.FINE, "Method not found: " + methodName, e); + return null; + } + }); + } + + public static Field getFieldFromCache(Class cls, String fieldName, boolean isPrivate) { + return fieldCache.computeIfAbsent(fieldName, k -> { + try { + if (isPrivate) { + Field field = cls.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } else { + return cls.getField(fieldName); + } + } catch (NoSuchFieldException e) { + logger.log(Level.FINE, "Field not found: " + fieldName, e); + return null; + } + }); + } +} diff --git a/appserver/web/war-util/src/main/java/org/glassfish/web/loader/WebappClassLoader.java b/appserver/web/war-util/src/main/java/org/glassfish/web/loader/WebappClassLoader.java index f053eb33e08..d8e665518f9 100644 --- a/appserver/web/war-util/src/main/java/org/glassfish/web/loader/WebappClassLoader.java +++ b/appserver/web/war-util/src/main/java/org/glassfish/web/loader/WebappClassLoader.java @@ -55,7 +55,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// Portions Copyright 2016-2023 Payara Foundation and/or its affiliates +// Portions Copyright 2016-2024 Payara Foundation and/or its affiliates package org.glassfish.web.loader; @@ -121,6 +121,7 @@ import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -131,11 +132,13 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.Predicate; import java.util.jar.Attributes; import java.util.jar.Attributes.Name; import java.util.jar.JarEntry; @@ -1172,6 +1175,18 @@ public boolean modified() { @Override public String toString() { StringBuilder sb = new StringBuilder(); + Predicate matchesInternal = str -> str.matches(".*generated/.*__.*"); + boolean isInternal = false; + if (repositoryURLs != null) { + isInternal = Arrays.stream(repositoryURLs).map(URL::toString) + .filter(matchesInternal).findAny().isPresent(); + } + if (canonicalLoaderDir != null && matchesInternal.test(canonicalLoaderDir)) { + isInternal = true; + } + if (isInternal) { + sb.append("(internal) "); + } sb.append("WebappClassLoader (delegate="); sb.append(delegate); if (repositoryURLs != null) { @@ -2036,6 +2051,8 @@ public void stop() throws Exception { // START SJSAS 6258619 ClassLoaderUtil.releaseLoader(this); // END SJSAS 6258619 + clearBeanELResolverCache(); + clearJaxRSCache(); synchronized(jarFilesLock) { started = false; @@ -2642,6 +2659,77 @@ private void clearReferencesRmiTargets() { } } + private void clearBeanELResolverCache() { + try { + Class elUtilsClass = CachingReflectionUtil.getClassFromCache("com.sun.faces.el.ELUtils", this); + if (elUtilsClass != null) { + clearBeanResolver(elUtilsClass); + } + } catch (Exception e) { + logger.log(Level.WARNING, "Error clearing BeanELResolver cache", e); + } + } + + private void clearBeanResolver(Class elUtilsClass) throws Exception { + Optional> elResolverClass = Optional.ofNullable(CachingReflectionUtil + .getClassFromCache("jakarta.el.BeanELResolver", this)); + Object resolver = CachingReflectionUtil.getFieldFromCache(elUtilsClass, "BEAN_RESOLVER", + false).get(null); + if (resolver != null && elResolverClass.isPresent()) { + logger.fine(String.format("Fields: %s", Arrays.stream(elResolverClass.get().getDeclaredFields()) + .map(Field::toString).collect(Collectors.toList()))); + Method clearPropertiesMethod = CachingReflectionUtil.getMethodFromCache(elResolverClass.get(), + "clearProperties", false, ClassLoader.class); + if (clearPropertiesMethod != null) { + clearPropertiesMethod.invoke(resolver, this); + } else { + clearBeanELResolverPropertiesCache(resolver, elResolverClass.get()); + } + } else { + logger.warning("BeanELResolver not found"); + } + } + + /** + * Workaround until clearProperties() is available in Jakarta EL + * @see Jakarta EL Pull Request + */ + private void clearBeanELResolverPropertiesCache(Object resolver, Class elResolverClass) throws Exception { + Optional> elResolverCacheClass = Optional.ofNullable(CachingReflectionUtil + .getClassFromCache("jakarta.el.BeanELResolver$SoftConcurrentHashMap", this)); + var propertiesField = Optional.ofNullable(CachingReflectionUtil + .getFieldFromCache(elResolverClass, "properties", true)); + @SuppressWarnings("unchecked") + ConcurrentHashMap, Object> properties = + (ConcurrentHashMap, Object>) propertiesField.get().get(resolver); + properties.entrySet().removeIf(entry -> entry.getKey().getClassLoader() == this); + var mapField = Optional.ofNullable(CachingReflectionUtil + .getFieldFromCache(elResolverCacheClass.get(), "map", true)); + @SuppressWarnings("unchecked") + ConcurrentHashMap, Object> map = + (ConcurrentHashMap, Object>) mapField.get().get(propertiesField.get().get(resolver)); + map.entrySet().removeIf(entry -> entry.getKey().getClassLoader() == this); + var cleanupMethod = Optional.ofNullable(CachingReflectionUtil + .getMethodFromCache(elResolverCacheClass.get(), "cleanup", true)); + cleanupMethod.get().invoke(propertiesField.get().get(resolver)); + } + + private void clearJaxRSCache() { + try { + Class cdiComponentProvider = CachingReflectionUtil + .getClassFromCache("org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider", this); + if (cdiComponentProvider != null) { + Field runtimeSpecificsField = CachingReflectionUtil.getFieldFromCache(cdiComponentProvider, + "runtimeSpecifics", true); + Object runtimeSpecifics = runtimeSpecificsField.get(null); + CachingReflectionUtil.getMethodFromCache(runtimeSpecifics.getClass(), + "clearJaxRsResource", true, ClassLoader.class) + .invoke(runtimeSpecifics, this); + } + } catch (Exception e) { + logger.log(Level.WARNING, "Error clearing Jax-Rs cache", e); + } + } /** * Clear the {@link ResourceBundle} cache of any bundles loaded by this diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterConfig.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterConfig.java index 91bd5a0de06..c748b5d846a 100644 --- a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterConfig.java +++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ApplicationFilterConfig.java @@ -55,7 +55,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// Portions Copyright [2019-2021] Payara Foundation and/or affiliates +// Portions Copyright [2019-2024] Payara Foundation and/or affiliates package org.apache.catalina.core; @@ -324,8 +324,9 @@ void release() { String msg = rb.getString(LogFacade.DO_AS_PRIVILEGE); log.log(Level.SEVERE, msg, ex); } - } else { + } else { filter.destroy(); + filterDef = null; } if (context != null) { diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ContainerBase.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ContainerBase.java index 30032acbeb5..2a783c96a57 100644 --- a/appserver/web/web-core/src/main/java/org/apache/catalina/core/ContainerBase.java +++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/ContainerBase.java @@ -62,9 +62,12 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; +import java.lang.ref.WeakReference; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -208,7 +211,7 @@ public Void run() { /** * The processor delay for this component. */ - protected int backgroundProcessorDelay = -1; + protected AtomicInteger backgroundProcessorDelay = new AtomicInteger(-1); /** @@ -317,13 +320,13 @@ public Void run() { /** * The background thread completion semaphore. */ - private volatile boolean threadDone = false; + private AtomicBoolean threadDone = new AtomicBoolean(); /** * The session background thread completion semaphore. */ - private volatile boolean threadSessionDone = false; + private AtomicBoolean threadSessionDone = new AtomicBoolean(); /** @@ -382,7 +385,7 @@ public void setDebug(int debug) { */ @Override public int getBackgroundProcessorDelay() { - return backgroundProcessorDelay; + return backgroundProcessorDelay.get(); } @@ -395,7 +398,7 @@ public int getBackgroundProcessorDelay() { */ @Override public void setBackgroundProcessorDelay(int delay) { - backgroundProcessorDelay = delay; + backgroundProcessorDelay.set(delay); } @@ -1734,12 +1737,13 @@ protected void threadStart() { if (thread != null) return; - if (backgroundProcessorDelay <= 0) + if (backgroundProcessorDelay.get() <= 0) return; - threadDone = false; + threadDone.set(false); String threadName = "ContainerBackgroundProcessor[" + toString() + "]"; - thread = new Thread(new ContainerBackgroundProcessor(), threadName); + thread = new Thread(new ContainerBackgroundProcessor(getMappingObject(), threadDone, + backgroundProcessorDelay), threadName); thread.setDaemon(true); thread.start(); @@ -1752,9 +1756,10 @@ protected void threadStart() { protected void threadSessionStart() { if (sessionThread != null || manager == null) return; - threadSessionDone = false; + threadSessionDone.set(false); String threadName = "ContainerBackgroundSessionProcessor[" + toString() + "]"; - sessionThread = new Thread(new ContainerBackgroundSessionProcessor(), threadName); + sessionThread = new Thread(new ContainerBackgroundSessionProcessor(getMappingObject(), manager, + threadSessionDone), threadName); sessionThread.setDaemon(true); sessionThread.start(); @@ -1770,7 +1775,7 @@ protected void threadStop() { if (thread == null) return; - threadDone = true; + threadDone.set(true); thread.interrupt(); try { thread.join(); @@ -1791,7 +1796,7 @@ protected void threadSessionStop() { return; } - this.threadSessionDone = true; + this.threadSessionDone.set(true); sessionThread.interrupt(); try { sessionThread.join(); @@ -1808,18 +1813,28 @@ protected void threadSessionStop() { * Private thread class to invoke the backgroundProcess method * of this container and its children after a fixed delay. */ - protected class ContainerBackgroundProcessor implements Runnable { + protected static class ContainerBackgroundProcessor implements Runnable { + private final WeakReference base; + private final AtomicBoolean threadDone; + private final AtomicInteger backgroundProcessorDelay; + + public ContainerBackgroundProcessor(Object base, + AtomicBoolean threadDone, AtomicInteger backgroundProcessorDelay) { + this.base = new WeakReference<>(base); + this.threadDone = threadDone; + this.backgroundProcessorDelay = backgroundProcessorDelay; + } @Override public void run() { - while (!threadDone) { + while (!threadDone.get() && base.get() != null) { try { - Thread.sleep(backgroundProcessorDelay * 1000L); + Thread.sleep(backgroundProcessorDelay.get() * 1000L); } catch (InterruptedException e) { // Ignore } - if (!threadDone) { - Container parent = (Container) getMappingObject(); + if (!threadDone.get()) { + Container parent = (Container) base.get(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (parent.getLoader() != null) { @@ -1856,20 +1871,29 @@ protected void processChildren(Container container, ClassLoader cl) { * Thread class to invoke the backgroundSessionUpdate method * of this container and its children after 500 milliseconds. */ - protected class ContainerBackgroundSessionProcessor implements Runnable { + protected static class ContainerBackgroundSessionProcessor implements Runnable { + private final WeakReference base; + private final WeakReference manager; + private final AtomicBoolean threadSessionDone; + + public ContainerBackgroundSessionProcessor(Object base, Manager manager, AtomicBoolean threadSessionDone) { + this.base = new WeakReference<>(base); + this.manager = new WeakReference<>(manager); + this.threadSessionDone = threadSessionDone; + } @Override public void run() { - if(manager != null) { - while (!threadSessionDone) { + if(manager.get() != null) { + while (!threadSessionDone.get() && base.get() != null) { try { //this will calculate the interval depending on the configured timeout from the application - Thread.sleep( (manager.getMaxInactiveInterval() / 2) * 1000L); + Thread.sleep( (manager.get().getMaxInactiveInterval() / 2) * 1000L); } catch (InterruptedException e) { // Ignore } - if (!threadSessionDone) { - Container parent = (Container) getMappingObject(); + if (!threadSessionDone.get()) { + Container parent = (Container) base.get(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (parent.getLoader() != null) { diff --git a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardEngine.java b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardEngine.java index e72709ebd46..a2e4b53ae9d 100644 --- a/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardEngine.java +++ b/appserver/web/web-core/src/main/java/org/apache/catalina/core/StandardEngine.java @@ -97,7 +97,7 @@ public StandardEngine() { } catch(Exception ex) { } // By default, the engine will hold the reloading thread - backgroundProcessorDelay = 10; + backgroundProcessorDelay.set(10); } diff --git a/appserver/web/web-glue/src/main/java/com/sun/enterprise/web/WebComponentInvocation.java b/appserver/web/web-glue/src/main/java/com/sun/enterprise/web/WebComponentInvocation.java index 4a134a1a50d..47f463bd1b9 100644 --- a/appserver/web/web-glue/src/main/java/com/sun/enterprise/web/WebComponentInvocation.java +++ b/appserver/web/web-glue/src/main/java/com/sun/enterprise/web/WebComponentInvocation.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2019-2022] [Payara Foundation and/or its affiliates] +// Portions Copyright [2019-2024] [Payara Foundation and/or its affiliates] package com.sun.enterprise.web; @@ -64,8 +64,8 @@ public WebComponentInvocation(WebModule wm, Object instance) { setComponentInvocationType( ComponentInvocation.ComponentInvocationType.SERVLET_INVOCATION); componentId = wm.getComponentId(); - jndiEnvironment = wm.getWebBundleDescriptor(); - container = wm; + setJNDIEnvironment(wm.getWebBundleDescriptor()); + setContainer(wm); this.instance = instance; setResourceTableKey(_getResourceTableKey()); diff --git a/core/core-parent/pom.xml b/core/core-parent/pom.xml index 2071bf75a4c..b88ddb4f71c 100644 --- a/core/core-parent/pom.xml +++ b/core/core-parent/pom.xml @@ -642,6 +642,8 @@ io.opentelemetry.extension io.opentelemetry.instrumentation fish.payara.shaded + org.glassfish.api.invocation + org.apache.catalina.core diff --git a/nucleus/common/common-util/src/main/java/com/sun/enterprise/loader/ASURLClassLoader.java b/nucleus/common/common-util/src/main/java/com/sun/enterprise/loader/ASURLClassLoader.java index 4f131b54612..1f9b249c51f 100644 --- a/nucleus/common/common-util/src/main/java/com/sun/enterprise/loader/ASURLClassLoader.java +++ b/nucleus/common/common-util/src/main/java/com/sun/enterprise/loader/ASURLClassLoader.java @@ -53,6 +53,7 @@ import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import org.glassfish.hk2.utilities.CleanerFactory; +import java.lang.ref.WeakReference; import java.net.*; import java.nio.file.Path; import java.security.*; @@ -435,7 +436,7 @@ private URL findResource0(final URLEntry res, *prevent the JDK's JarURLConnection caching from *locking the jar file until JVM exit. */ - InternalURLStreamHandler handler = new InternalURLStreamHandler(res, name); + InternalURLStreamHandler handler = new InternalURLStreamHandler(this, res, name); // Create a new sub URL from the resource URL (i.e. res.source). To avoid double encoding // (see https://glassfish.dev.java.net/issues/show_bug.cgi?id=13045) @@ -874,7 +875,7 @@ public InputStream getResourceAsStream(final String name) { */ if (stream != null) { if (! (stream instanceof SentinelInputStream)) { - stream = new SentinelInputStream(stream); + stream = new SentinelInputStream(this, stream); } } return stream; @@ -1097,9 +1098,10 @@ private synchronized void closeOpenStreams() { * @author vtsyganok * @author tjquinn */ - protected final class SentinelInputStream extends FilterInputStream { + protected static final class SentinelInputStream extends FilterInputStream { private volatile boolean closed = false; private volatile Throwable throwable; + private final WeakReference loader; /** * Constructs new FilteredInputStream which reports InputStreams not closed properly. @@ -1108,10 +1110,11 @@ protected final class SentinelInputStream extends FilterInputStream { * * @param in - InputStream to be wrapped */ - protected SentinelInputStream(final InputStream in) { + protected SentinelInputStream(ASURLClassLoader loader, final InputStream in) { super(in); throwable = new Throwable(); - getStreams().add(this); + this.loader = new WeakReference<>(loader); + loader.streams.add(this); registerStopEvent(); } @@ -1134,15 +1137,6 @@ public final void registerStopEvent() { CleanerFactory.create().register(this, () -> closeWithWarning()); } - /** - * Returns the vector of open streams; creates it if needed. - * - * @return Vector holding open streams - */ - private List getStreams() { - return streams; - } - private synchronized void _close() throws IOException { if ( closed ) { return; @@ -1151,7 +1145,9 @@ private synchronized void _close() throws IOException { closed = true; throwable = null; - getStreams().remove(this); + if (loader.get() != null) { + loader.get().streams.remove(this); + } super.close(); } @@ -1186,9 +1182,10 @@ private void report(Throwable localThrowable) { * * @author fkieviet */ - private class InternalJarURLConnection extends JarURLConnection { + private static class InternalJarURLConnection extends JarURLConnection { private final URLEntry mRes; private final String mName; + private final WeakReference loader; /** * Constructor @@ -1198,11 +1195,12 @@ private class InternalJarURLConnection extends JarURLConnection { * @param name String * @throws MalformedURLException from super class */ - public InternalJarURLConnection(URL url, URLEntry res, String name) + public InternalJarURLConnection(ASURLClassLoader loader, URL url, URLEntry res, String name) throws MalformedURLException { super(url); mRes = res; mName = name; + this.loader = new WeakReference<>(loader); } /** @@ -1235,7 +1233,7 @@ public InputStream getInputStream() throws IOException { if (entry == null) { throw new IOException("no entry called " + mName + " found in " + mRes.source); } - return new SentinelInputStream(mRes.zip.getInputStream(entry)); + return new SentinelInputStream(loader.get(), mRes.zip.getInputStream(entry)); } } @@ -1248,10 +1246,11 @@ public InputStream getInputStream() throws IOException { * * @author fkieviet */ - private class InternalURLStreamHandler extends URLStreamHandler { + private static class InternalURLStreamHandler extends URLStreamHandler { /** must be 'volatile' for thread visibility */ private volatile URL mURL; private final URLEntry mRes; + private final WeakReference loader; /** * Constructor @@ -1259,8 +1258,9 @@ private class InternalURLStreamHandler extends URLStreamHandler { * @param res URLEntry * @param name String */ - public InternalURLStreamHandler(URLEntry res, String name) { + public InternalURLStreamHandler(ASURLClassLoader loader, URLEntry res, String name) { mRes = res; + this.loader = new WeakReference<>(loader); } /** @@ -1282,7 +1282,7 @@ protected URLConnection openConnection(final URL u) throws IOException { assert (entryName.startsWith("/")); entryName = entryName.substring(1); } - return new InternalJarURLConnection(u, mRes, entryName); + return new InternalJarURLConnection(loader.get(), u, mRes, entryName); } catch (URISyntaxException e) { throw new IOException(e); } diff --git a/nucleus/common/glassfish-api/src/main/java/org/glassfish/api/invocation/ComponentInvocation.java b/nucleus/common/glassfish-api/src/main/java/org/glassfish/api/invocation/ComponentInvocation.java index 594ab913dcc..18130931752 100644 --- a/nucleus/common/glassfish-api/src/main/java/org/glassfish/api/invocation/ComponentInvocation.java +++ b/nucleus/common/glassfish-api/src/main/java/org/glassfish/api/invocation/ComponentInvocation.java @@ -37,12 +37,13 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2019-2020] [Payara Foundation and/or its affiliates] +// Portions Copyright [2019-2024] [Payara Foundation and/or its affiliates] package org.glassfish.api.invocation; import static org.glassfish.api.invocation.ComponentInvocation.ComponentInvocationType.UN_INITIALIZED; +import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; @@ -80,9 +81,9 @@ public enum ComponentInvocationType { /** * ServletContext for servlet, Container for EJB */ - public Object container; + private WeakReference container; - public Object jndiEnvironment; + private WeakReference jndiEnvironment; public String componentId; @@ -117,13 +118,13 @@ public enum ComponentInvocationType { protected String registrationName; public ComponentInvocation() { - + container = new WeakReference<>(null); } public ComponentInvocation(String componentId, ComponentInvocationType invocationType, Object container, String appName, String moduleName, String registrationName) { this.componentId = componentId; this.invocationType = invocationType; - this.container = container; + this.container = new WeakReference<>(container); this.appName = appName; this.moduleName = moduleName; this.registrationName = registrationName; @@ -133,7 +134,7 @@ public ComponentInvocation(String componentId, ComponentInvocationType invocatio this.componentId = componentId; this.invocationType = invocationType; this.instance = instance; - this.container = container; + this.container = new WeakReference<>(container); this.transaction = transaction; } @@ -202,31 +203,34 @@ public void setComponentId(String componentId) { } public Object getJndiEnvironment() { - return jndiEnvironment; + return getJNDIEnvironment(); } public void setJndiEnvironment(Object jndiEnvironment) { - this.jndiEnvironment = jndiEnvironment; + setJNDIEnvironment(jndiEnvironment); } public void setJNDIEnvironment(Object val) { - jndiEnvironment = val; + jndiEnvironment = new WeakReference<>(val); } public Object getJNDIEnvironment() { - return jndiEnvironment; + if (jndiEnvironment == null) { + return null; + } + return jndiEnvironment.get(); } public Object getContainer() { - return container; + return container.get(); } public void setContainer(Object container) { - this.container = container; + this.container = new WeakReference<>(container); } public Object getContainerContext() { - return container; + return getContainer(); } public Object getTransaction() { @@ -391,7 +395,7 @@ public String toString() { str.append("\tcomponentId=").append(componentId).append('\n'); str.append("\ttype=").append(invocationType).append('\n'); str.append("\tinstance=").append(instanceName != null ? instanceName : String.valueOf(instance)).append('\n'); - str.append("\tcontainer=").append(container).append('\n'); + str.append("\tcontainer=").append(getContainer()).append('\n'); str.append("\tappName=").append(appName).append('\n'); return str.toString(); } diff --git a/nucleus/deployment/admin/src/main/java/org/glassfish/deployment/admin/DeployCommandSupplementalInfo.java b/nucleus/deployment/admin/src/main/java/org/glassfish/deployment/admin/DeployCommandSupplementalInfo.java index 55371eb75cf..babbeb08081 100644 --- a/nucleus/deployment/admin/src/main/java/org/glassfish/deployment/admin/DeployCommandSupplementalInfo.java +++ b/nucleus/deployment/admin/src/main/java/org/glassfish/deployment/admin/DeployCommandSupplementalInfo.java @@ -37,10 +37,12 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2024] Payara Foundation and/or affiliates package org.glassfish.deployment.admin; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.Collection; import java.util.List; import org.glassfish.api.admin.AccessRequired.AccessCheck; @@ -54,12 +56,12 @@ */ public class DeployCommandSupplementalInfo implements Serializable{ - private transient ExtendedDeploymentContext dc = null; + private transient WeakReference dc = null; private List previousTargets = null; private Collection accessChecks = null; public void setDeploymentContext(final ExtendedDeploymentContext dc) { - this.dc = dc; + this.dc = new WeakReference<>(dc); /* * Save the previous targets (if any), because the deploy command * processing will clear the transient app metadata which is @@ -81,7 +83,7 @@ public Collection getAccessChecks() { } public ExtendedDeploymentContext deploymentContext() { - return dc; + return dc != null ? dc.get() : null; } public List previousTargets() {