diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 422449a0536..75aac07d2a0 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -18,9 +18,43 @@ jobs: - name: List ports run: ss -tulpn - name: Build with Maven + id: maven-build # qa skips documentation - we check it on Jenkins CI # We skip checkstyle too - we check it on Jenkins CI - run: mvn -B -e -ntp install -Pstaging -Pqa '-Dcheckstyle.skip=true' + run: | + set -o pipefail + mvn -B -e -ntp install -Pstaging -Pqa '-Dcheckstyle.skip=true' 2>&1 | tee build.log + - name: Extract build failure info + if: failure() && steps.maven-build.outcome == 'failure' + run: | + echo "## Build Failure Summary" >> $GITHUB_STEP_SUMMARY + echo "### Last 100 lines of Maven output:" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + tail -100 build.log >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + + echo "### Error Analysis:" >> $GITHUB_STEP_SUMMARY + if grep -q "BUILD FAILURE" build.log; then + echo "- ❌ Maven build failed" >> $GITHUB_STEP_SUMMARY + # Extract the failure reason + echo "### Failure Reason:" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + grep -A 10 "BUILD FAILURE" build.log | head -15 >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + fi + if grep -q "COMPILATION ERROR" build.log; then + echo "- ❌ Compilation errors detected" >> $GITHUB_STEP_SUMMARY + fi + if grep -q "Tests run:.*Failures: [1-9]" build.log; then + echo "- ❌ Test failures detected" >> $GITHUB_STEP_SUMMARY + fi + - name: Upload build log + uses: actions/upload-artifact@v4 + if: failure() + with: + name: build-log + path: build.log + retention-days: 3 - name: Upload server logs uses: actions/upload-artifact@v4 if: failure() diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index c203ba4a969..f27ad4e05e9 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -16,11 +16,48 @@ jobs: distribution: 'temurin' cache: maven - name: Build with Maven + id: maven-build # qa skips documentation - we check it on Jenkins CI # We skip checkstyle too - we check it on Jenkins CI - run: mvn -B -e -ntp install -Pstaging -Pqa '-Dcheckstyle.skip=true' -Pfastest + run: | + set -o pipefail + mvn -B -e -ntp install -Pstaging -Pqa '-Dcheckstyle.skip=true' -Pfastest 2>&1 | tee build.log - name: Test Starts - run: mvn -B -e -ntp install -Pstaging '-Dcheckstyle.skip=true' -pl :glassfish-itest-tools + id: test-starts + run: | + set -o pipefail + mvn -B -e -ntp install -Pstaging '-Dcheckstyle.skip=true' -pl :glassfish-itest-tools 2>&1 | tee -a build.log + - name: Extract build failure info + if: failure() && (steps.maven-build.outcome == 'failure' || steps.test-starts.outcome == 'failure') + run: | + echo "## Build Failure Summary" >> $GITHUB_STEP_SUMMARY + echo "### Last 100 lines of Maven output:" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + tail -100 build.log >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + + echo "### Error Analysis:" >> $GITHUB_STEP_SUMMARY + if grep -q "BUILD FAILURE" build.log; then + echo "- ❌ Maven build failed" >> $GITHUB_STEP_SUMMARY + # Extract the failure reason + echo "### Failure Reason:" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + grep -A 10 "BUILD FAILURE" build.log | head -15 >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + fi + if grep -q "COMPILATION ERROR" build.log; then + echo "- ❌ Compilation errors detected" >> $GITHUB_STEP_SUMMARY + fi + if grep -q "Tests run:.*Failures: [1-9]" build.log; then + echo "- ❌ Test failures detected" >> $GITHUB_STEP_SUMMARY + fi + - name: Upload build log + uses: actions/upload-artifact@v4 + if: failure() + with: + name: build-log + path: build.log + retention-days: 3 - name: Upload server logs uses: actions/upload-artifact@v4 if: failure() diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 1e80a4c04fd..87bcd766b44 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -16,9 +16,44 @@ jobs: distribution: 'temurin' cache: maven - name: Build with Maven + id: maven-build # qa skips documentation - we check it on Jenkins CI # We skip checkstyle too - we check it on Jenkins CI - run: mvn -B -e -ntp install -Pstaging -Pqa '-Dcheckstyle.skip=true' + run: | + mvn -B -e -ntp install -Pstaging -Pqa '-Dcheckstyle.skip=true' 2>&1 | Tee-Object build.log + - name: Extract build failure info + if: failure() && steps.maven-build.outcome == 'failure' + shell: pwsh + run: | + echo "## Build Failure Summary" >> $env:GITHUB_STEP_SUMMARY + echo "### Last 100 lines of Maven output:" >> $env:GITHUB_STEP_SUMMARY + echo '```' >> $env:GITHUB_STEP_SUMMARY + Get-Content build.log -Tail 100 >> $env:GITHUB_STEP_SUMMARY + echo '```' >> $env:GITHUB_STEP_SUMMARY + + echo "### Error Analysis:" >> $env:GITHUB_STEP_SUMMARY + $content = Get-Content build.log -Raw + if ($content -match "BUILD FAILURE") { + echo "- ❌ Maven build failed" >> $env:GITHUB_STEP_SUMMARY + # Extract the failure reason + echo "### Failure Reason:" >> $env:GITHUB_STEP_SUMMARY + echo '```' >> $env:GITHUB_STEP_SUMMARY + ($content | Select-String -Pattern "BUILD FAILURE" -Context 0,10).Context.PostContext[0..14] -join "`n" >> $env:GITHUB_STEP_SUMMARY + echo '```' >> $env:GITHUB_STEP_SUMMARY + } + if ($content -match "COMPILATION ERROR") { + echo "- ❌ Compilation errors detected" >> $env:GITHUB_STEP_SUMMARY + } + if ($content -match "Tests run:.*Failures: [1-9]") { + echo "- ❌ Test failures detected" >> $env:GITHUB_STEP_SUMMARY + } + - name: Upload build log + uses: actions/upload-artifact@v4 + if: failure() + with: + name: build-log + path: build.log + retention-days: 3 - name: Upload server logs uses: actions/upload-artifact@v4 if: failure() diff --git a/appserver/admin/template/src/main/resources/config/domain.xml b/appserver/admin/template/src/main/resources/config/domain.xml index c30fa534715..e1913bc4a58 100644 --- a/appserver/admin/template/src/main/resources/config/domain.xml +++ b/appserver/admin/template/src/main/resources/config/domain.xml @@ -223,6 +223,7 @@ --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-exports=java.base/jdk.internal.vm.annotation=ALL-UNNAMED --add-opens=java.base/jdk.internal.vm.annotation=ALL-UNNAMED + --add-exports=java.base/jdk.internal.loader=ALL-UNNAMED -Djdk.attach.allowAttachSelf=true @@ -421,6 +422,7 @@ --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-exports=java.base/jdk.internal.vm.annotation=ALL-UNNAMED --add-opens=java.base/jdk.internal.vm.annotation=ALL-UNNAMED + --add-exports=java.base/jdk.internal.loader=ALL-UNNAMED -Djdk.attach.allowAttachSelf=true diff --git a/appserver/extras/embedded/all/pom.xml b/appserver/extras/embedded/all/pom.xml index 70bef2e780f..ffd5d797b10 100644 --- a/appserver/extras/embedded/all/pom.xml +++ b/appserver/extras/embedded/all/pom.xml @@ -127,6 +127,17 @@ + + org.codehaus.gmaven + groovy-maven-plugin + + + merge-manifest-values-to-properties + prepare-package + + + + maven-assembly-plugin @@ -166,8 +177,9 @@ true org.glassfish.main.embedded.all org.glassfish.runnablejar.UberMain - java.base/java.lang java.base/java.io java.base/java.util java.base/sun.nio.fs java.base/sun.net.www.protocol.jrt java.naming/javax.naming.spi java.rmi/sun.rmi.transport jdk.management/com.sun.management.internal java.base/jdk.internal.vm.annotation - java.naming/com.sun.jndi.ldap java.base/jdk.internal.vm.annotation + ${glassfish.embedded.add-opens} + ${glassfish.embedded.add-exports} + ${probe.provider.class.names} diff --git a/appserver/extras/embedded/pom.xml b/appserver/extras/embedded/pom.xml index 0e265f7c36d..8dd40eb9927 100644 --- a/appserver/extras/embedded/pom.xml +++ b/appserver/extras/embedded/pom.xml @@ -16,7 +16,6 @@ SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 --> - 4.0.0 @@ -31,6 +30,11 @@ GlassFish Embedded modules + + java.base/java.lang java.base/java.io java.base/java.util java.base/sun.nio.fs java.base/sun.net.www.protocol.jrt java.naming/javax.naming.spi java.rmi/sun.rmi.transport jdk.management/com.sun.management.internal java.base/jdk.internal.vm.annotation + java.naming/com.sun.jndi.ldap java.base/jdk.internal.vm.annotation java.base/jdk.internal.loader + + common all @@ -52,4 +56,62 @@ + + + + + org.codehaus.gmaven + groovy-maven-plugin + + + + merge-manifest-values-to-properties + + execute + + + + import java.util.jar.JarFile + import java.util.jar.Manifest + + def probeProviders = [] as Set + + project.artifacts.each { artifact -> + if (artifact.type == 'jar') { + try { + def jar = new JarFile(artifact.file) + def manifest = jar.manifest + if (manifest) { + def probeValue = manifest.mainAttributes.getValue('probe-provider-class-names') + if (probeValue) { + probeProviders.add(probeValue) + } + } + jar.close() + } catch (Exception e) { + // Ignore invalid JARs + } + } + } + + if (probeProviders) { + project.properties['probe.provider.class.names'] = probeProviders.join(',') + println "Found ${probeProviders.size()} probe providers in project dependencies" + } else { + println "No probe providers found in project dependencies" + } + + + + + + + + + diff --git a/appserver/extras/embedded/shell/glassfish-embedded-static-shell/pom.xml b/appserver/extras/embedded/shell/glassfish-embedded-static-shell/pom.xml index 59d9311c990..2bc22191d43 100755 --- a/appserver/extras/embedded/shell/glassfish-embedded-static-shell/pom.xml +++ b/appserver/extras/embedded/shell/glassfish-embedded-static-shell/pom.xml @@ -1,7 +1,7 @@ -Djdk.attach.allowAttachSelf=true @@ -342,6 +343,7 @@ --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED + --add-exports=java.base/jdk.internal.loader=ALL-UNNAMED -Djdk.attach.allowAttachSelf=true diff --git a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GlassfishNetworkListener.java b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GlassfishNetworkListener.java index 5fa6c749962..353636f64ec 100644 --- a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GlassfishNetworkListener.java +++ b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GlassfishNetworkListener.java @@ -83,6 +83,11 @@ public NetworkListener getNetworkListener() { @Override public void stop() throws IOException { + stop(true); + } + + @Override + public void stop(boolean lastOne) throws IOException { ServiceLocator locator = grizzlyService.getServiceLocator(); IndexedFilter removeFilter = BuilderHelper.createNameAndContractFilter(Mapper.class.getName(), (address.toString() + port)); @@ -94,7 +99,13 @@ public void stop() throws IOException { config.commit(); - unregisterMonitoringStatsProviders(); + // Do not call unregisterMonitoringStatsProviders() + // - providers are shared by all listeners, they would be removed also for other listeners. + // We could add a rule to unregister only if there are no other listeners. + if (lastOne) { + unregisterMonitoringStatsProviders(); + } + super.stop(); } diff --git a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GrizzlyProxy.java b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GrizzlyProxy.java index 9a5d6013f18..2d173aa013f 100644 --- a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GrizzlyProxy.java +++ b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GrizzlyProxy.java @@ -143,6 +143,14 @@ public void stop() throws IOException { grizzlyListener.stop(); } + /** + * Stops the Grizzly service and shared resources if last one. + */ + @Override + public void stop(boolean lastOne) throws IOException { + grizzlyListener.stop(lastOne); + } + @Override public void destroy() { grizzlyListener.destroy(); diff --git a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GrizzlyService.java b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GrizzlyService.java index d4e52c5e4f2..dcb4ba51817 100644 --- a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GrizzlyService.java +++ b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/GrizzlyService.java @@ -203,7 +203,8 @@ private NetworkProxy getNetworkProxy(String id) { public boolean removeNetworkProxy(NetworkProxy proxy) { if (proxy != null) { try { - proxy.stop(); + boolean lastOne = proxies.size() == 1; + proxy.stop(lastOne); } catch (IOException e) { LOGGER.log(WARNING, KernelLoggerInfo.grizzlyStopProxy, e); } diff --git a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/NetworkProxy.java b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/NetworkProxy.java index b31d525e817..2c31dccdeb8 100644 --- a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/NetworkProxy.java +++ b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/NetworkProxy.java @@ -42,6 +42,14 @@ public interface NetworkProxy extends EndpointMapper{ */ void stop() throws IOException; + /** + * Stop the proxy and remove all shared resources if it's the last proxy. + * @param lastOne Whether it's the last proxy. + * {@code false} if other proxies still exist and depend on the shared resources + */ + default void stop(boolean lastOne) throws IOException { + stop(); + } /** * Start the proxy. diff --git a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/ThreadPoolMonitor.java b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/ThreadPoolMonitor.java index d6f911f298b..3a26367be4a 100644 --- a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/ThreadPoolMonitor.java +++ b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/ThreadPoolMonitor.java @@ -17,6 +17,7 @@ package com.sun.enterprise.v3.services.impl.monitor; import com.sun.enterprise.v3.services.impl.monitor.stats.ConnectionQueueStatsProvider; +import com.sun.enterprise.v3.services.impl.monitor.stats.ThreadPoolStats; import com.sun.enterprise.v3.services.impl.monitor.stats.ThreadPoolStatsProvider; import org.glassfish.grizzly.threadpool.AbstractThreadPool; @@ -30,17 +31,19 @@ public class ThreadPoolMonitor implements ThreadPoolProbe { private final GrizzlyMonitoring grizzlyMonitoring; private final String monitoringId; + private final ThreadPoolStats stats; public ThreadPoolMonitor(GrizzlyMonitoring grizzlyMonitoring, String monitoringId, ThreadPoolConfig config) { this.grizzlyMonitoring = grizzlyMonitoring; this.monitoringId = monitoringId; + this.stats = new ThreadPoolStats(config); if (grizzlyMonitoring != null) { final ThreadPoolStatsProvider threadPoolStatsProvider = grizzlyMonitoring.getThreadPoolStatsProvider(monitoringId); if (threadPoolStatsProvider != null) { - threadPoolStatsProvider.setStatsObject(config); + threadPoolStatsProvider.setStatsObject(stats); threadPoolStatsProvider.reset(); } @@ -63,59 +66,97 @@ public void onThreadPoolStopEvent(AbstractThreadPool threadPool) { @Override public void onThreadAllocateEvent(AbstractThreadPool threadPool, Thread thread) { + stats.currentThreadCount = threadPool.getSize(); grizzlyMonitoring.getThreadPoolProbeProvider().threadAllocatedEvent( monitoringId, threadPool.getConfig().getPoolName(), thread.getId()); + grizzlyMonitoring.getThreadPoolProbeProvider().setCurrentThreadCountEvent( + monitoringId, threadPool.getConfig().getPoolName(), + threadPool.getSize()); } @Override public void onThreadReleaseEvent(AbstractThreadPool threadPool, Thread thread) { + stats.currentThreadCount = threadPool.getSize(); grizzlyMonitoring.getThreadPoolProbeProvider().threadReleasedEvent( monitoringId, threadPool.getConfig().getPoolName(), thread.getId()); + grizzlyMonitoring.getThreadPoolProbeProvider().setCurrentThreadCountEvent( + monitoringId, threadPool.getConfig().getPoolName(), + threadPool.getSize()); } @Override public void onMaxNumberOfThreadsEvent(AbstractThreadPool threadPool, int maxNumberOfThreads) { + stats.currentThreadCount = threadPool.getSize(); grizzlyMonitoring.getThreadPoolProbeProvider().maxNumberOfThreadsReachedEvent( monitoringId, threadPool.getConfig().getPoolName(), maxNumberOfThreads); + grizzlyMonitoring.getThreadPoolProbeProvider().setCurrentThreadCountEvent( + monitoringId, threadPool.getConfig().getPoolName(), + threadPool.getSize()); } @Override public void onTaskDequeueEvent(AbstractThreadPool threadPool, Runnable task) { + long currentBusyThreadCount = stats.currentBusyThreadCount.incrementAndGet(); + stats.currentThreadCount = threadPool.getSize(); grizzlyMonitoring.getThreadPoolProbeProvider().threadDispatchedFromPoolEvent( monitoringId, threadPool.getConfig().getPoolName(), - Thread.currentThread().getId()); + Thread.currentThread().getId(), + currentBusyThreadCount); grizzlyMonitoring.getConnectionQueueProbeProvider().onTaskDequeuedEvent( monitoringId, task.getClass().getName()); + grizzlyMonitoring.getThreadPoolProbeProvider().setCurrentThreadCountEvent( + monitoringId, threadPool.getConfig().getPoolName(), + threadPool.getSize()); } @Override public void onTaskCancelEvent(AbstractThreadPool threadPool, Runnable task) { + long currentBusyThreadCount = stats.currentBusyThreadCount.decrementAndGet(); + stats.currentThreadCount = threadPool.getSize(); // when dequeued task is cancelled - we have to "return" the thread, that // we marked as dispatched from the pool grizzlyMonitoring.getThreadPoolProbeProvider().threadReturnedToPoolEvent( monitoringId, threadPool.getConfig().getPoolName(), - Thread.currentThread().getId()); + Thread.currentThread().getId(), + currentBusyThreadCount); + grizzlyMonitoring.getThreadPoolProbeProvider().setCurrentThreadCountEvent( + monitoringId, threadPool.getConfig().getPoolName(), + threadPool.getSize()); } @Override public void onTaskCompleteEvent(AbstractThreadPool threadPool, Runnable task) { + long currentBusyThreadCount = stats.currentBusyThreadCount.decrementAndGet(); + stats.currentThreadCount = threadPool.getSize(); grizzlyMonitoring.getThreadPoolProbeProvider().threadReturnedToPoolEvent( monitoringId, threadPool.getConfig().getPoolName(), - Thread.currentThread().getId()); + Thread.currentThread().getId(), + currentBusyThreadCount); + grizzlyMonitoring.getThreadPoolProbeProvider().setCurrentThreadCountEvent( + monitoringId, threadPool.getConfig().getPoolName(), + threadPool.getSize()); } @Override public void onTaskQueueEvent(AbstractThreadPool threadPool, Runnable task) { + stats.currentThreadCount = threadPool.getSize(); grizzlyMonitoring.getConnectionQueueProbeProvider().onTaskQueuedEvent( monitoringId, task.getClass().getName()); + grizzlyMonitoring.getThreadPoolProbeProvider().setCurrentThreadCountEvent( + monitoringId, threadPool.getConfig().getPoolName(), + threadPool.getSize()); } @Override public void onTaskQueueOverflowEvent(AbstractThreadPool threadPool) { + stats.currentThreadCount = threadPool.getSize(); grizzlyMonitoring.getConnectionQueueProbeProvider().onTaskQueueOverflowEvent( monitoringId); + grizzlyMonitoring.getThreadPoolProbeProvider().setCurrentThreadCountEvent( + monitoringId, threadPool.getConfig().getPoolName(), + threadPool.getSize()); } } diff --git a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/probes/ThreadPoolProbeProvider.java b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/probes/ThreadPoolProbeProvider.java index 33eeb947bc4..52feb47639a 100644 --- a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/probes/ThreadPoolProbeProvider.java +++ b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/probes/ThreadPoolProbeProvider.java @@ -68,12 +68,20 @@ public void maxNumberOfThreadsReachedEvent( public void threadDispatchedFromPoolEvent( @ProbeParam("monitoringId") String monitoringId, @ProbeParam("threadPoolName") String threadPoolName, - @ProbeParam("threadId") long threadId) {} + @ProbeParam("threadId") long threadId, + @ProbeParam("busyThreadCount") long busyThreadCount) {} @Probe(name="threadReturnedToPoolEvent") public void threadReturnedToPoolEvent( @ProbeParam("monitoringId") String monitoringId, @ProbeParam("threadPoolName") String threadPoolName, - @ProbeParam("threadId") long threadId) {} + @ProbeParam("threadId") long threadId, + @ProbeParam("busyThreadCount") long busyThreadCount) {} + + @Probe(name="setCurrentThreadCountEvent") + public void setCurrentThreadCountEvent( + @ProbeParam("monitoringId") String monitoringId, + @ProbeParam("threadPoolName") String threadPoolName, + @ProbeParam("currentThreadCount") int currentThreadCount) {} } diff --git a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/stats/ThreadPoolStats.java b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/stats/ThreadPoolStats.java new file mode 100644 index 00000000000..83a0371f037 --- /dev/null +++ b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/stats/ThreadPoolStats.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package com.sun.enterprise.v3.services.impl.monitor.stats; + +import java.util.concurrent.atomic.AtomicLong; + +import org.glassfish.grizzly.threadpool.ThreadPoolConfig; + +/** + * An object to hold thread pool statistics. Shared by ThreadPoolMonitor and stats providers (probe listeners), + * as some stats should be preserved even if the listeners is down. + * For example, thread counters, if there's no reliable way to find out the actual thread numbers from the thread pool (like busy threads). + * + * @author Ondro Mihalyi + */ +public class ThreadPoolStats { + + public ThreadPoolConfig threadPoolConfig; + + public volatile long currentThreadCount; + + public AtomicLong currentBusyThreadCount = new AtomicLong(); + + public ThreadPoolStats(ThreadPoolConfig threadPoolConfig) { + this.threadPoolConfig = threadPoolConfig; + } + +} diff --git a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/stats/ThreadPoolStatsProvider.java b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/stats/ThreadPoolStatsProvider.java index d20b4e19cff..0d213ece593 100644 --- a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/stats/ThreadPoolStatsProvider.java +++ b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/stats/ThreadPoolStatsProvider.java @@ -25,7 +25,6 @@ import org.glassfish.gmbal.Description; import org.glassfish.gmbal.ManagedAttribute; import org.glassfish.gmbal.ManagedObject; -import org.glassfish.grizzly.threadpool.ThreadPoolConfig; /** * Thread Pool statistics @@ -45,7 +44,7 @@ public class ThreadPoolStatsProvider implements StatsProvider { protected final CountStatisticImpl currentThreadCount = new CountStatisticImpl("CurrentThreadCount", "count", "Provides the number of request processing threads currently in the listener thread pool"); protected final CountStatisticImpl currentThreadsBusy = new CountStatisticImpl("CurrentThreadsBusy", "count", "Provides the number of request processing threads currently in use in the listener thread pool serving requests"); - protected volatile ThreadPoolConfig threadPoolConfig; + protected volatile ThreadPoolStats threadPoolStats; public ThreadPoolStatsProvider(String name) { this.name = name; @@ -53,15 +52,15 @@ public ThreadPoolStatsProvider(String name) { @Override public Object getStatsObject() { - return threadPoolConfig; + return threadPoolStats; } @Override public void setStatsObject(Object object) { - if (object instanceof ThreadPoolConfig) { - threadPoolConfig = (ThreadPoolConfig) object; + if (object instanceof ThreadPoolStats) { + threadPoolStats = (ThreadPoolStats) object; } else { - threadPoolConfig = null; + threadPoolStats = null; } } @@ -86,12 +85,18 @@ public CountStatistic getTotalExecutedTasksCount() { @ManagedAttribute(id = "currentthreadcount") @Description("Provides the number of request processing threads currently in the listener thread pool") public CountStatistic getCurrentThreadCount() { + if (threadPoolStats != null) { + currentThreadCount.setCount(threadPoolStats.currentThreadCount); + } return currentThreadCount; } @ManagedAttribute(id = "currentthreadsbusy") @Description("Provides the number of request processing threads currently in use in the listener thread pool serving requests.") public CountStatistic getCurrentThreadsBusy() { + if (threadPoolStats != null) { + currentThreadsBusy.setCount(threadPoolStats.currentBusyThreadCount.get()); + } return currentThreadsBusy; } @@ -143,10 +148,11 @@ public void threadReleasedEvent( public void threadDispatchedFromPoolEvent( @ProbeParam("monitoringId") String monitoringId, @ProbeParam("threadPoolName") String threadPoolName, - @ProbeParam("threadId") long threadId) { + @ProbeParam("threadId") long threadId, + @ProbeParam("busyThreadCount") long busyThreadCount) { if (name.equals(monitoringId)) { - currentThreadsBusy.increment(); + currentThreadsBusy.setCount(busyThreadCount); } } @@ -154,23 +160,35 @@ public void threadDispatchedFromPoolEvent( public void threadReturnedToPoolEvent( @ProbeParam("monitoringId") String monitoringId, @ProbeParam("threadPoolName") String threadPoolName, - @ProbeParam("threadId") long threadId) { + @ProbeParam("threadId") long threadId, + @ProbeParam("busyThreadCount") long busyThreadCount) { if (name.equals(monitoringId)) { totalExecutedTasksCount.increment(); - currentThreadsBusy.decrement(); + currentThreadsBusy.setCount(busyThreadCount); + } + } + + @ProbeListener("glassfish:kernel:thread-pool:setCurrentThreadCountEvent") + public void setCurrentThreadCountEvent( + @ProbeParam("monitoringId") String monitoringId, + @ProbeParam("threadPoolName") String threadPoolName, + @ProbeParam("currentThreadCount") int currentThreadCount) { + if (name.equals(monitoringId)) { + this.currentThreadCount.setCount(currentThreadCount); } } @Reset public void reset() { - if (threadPoolConfig != null) { - maxThreadsCount.setCount(threadPoolConfig.getMaxPoolSize()); - coreThreadsCount.setCount(threadPoolConfig.getCorePoolSize()); - currentThreadCount.setCount(0); - currentThreadsBusy.setCount(0); + if (threadPoolStats != null) { + if (threadPoolStats.threadPoolConfig != null) { + maxThreadsCount.setCount(threadPoolStats.threadPoolConfig.getMaxPoolSize()); + coreThreadsCount.setCount(threadPoolStats.threadPoolConfig.getCorePoolSize()); + } + currentThreadCount.setCount(threadPoolStats.currentThreadCount); + currentThreadsBusy.setCount(threadPoolStats.currentBusyThreadCount.get()); } - totalExecutedTasksCount.setCount(0); } } diff --git a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/stats/ThreadPoolStatsProviderGlobal.java b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/stats/ThreadPoolStatsProviderGlobal.java index cee2c0c0460..a8e925d9074 100644 --- a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/stats/ThreadPoolStatsProviderGlobal.java +++ b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/services/impl/monitor/stats/ThreadPoolStatsProviderGlobal.java @@ -76,19 +76,29 @@ public void threadReleasedEvent( public void threadDispatchedFromPoolEvent( @ProbeParam("monitoringId") String monitoringId, @ProbeParam("threadPoolName") String threadPoolName, - @ProbeParam("threadId") long threadId) { + @ProbeParam("threadId") long threadId, + @ProbeParam("busyThreadCount") long busyThreadCount) { - currentThreadsBusy.increment(); + currentThreadsBusy.setCount(busyThreadCount); } @ProbeListener("glassfish:kernel:thread-pool:threadReturnedToPoolEvent") public void threadReturnedToPoolEvent( @ProbeParam("monitoringId") String monitoringId, @ProbeParam("threadPoolName") String threadPoolName, - @ProbeParam("threadId") long threadId) { + @ProbeParam("threadId") long threadId, + @ProbeParam("busyThreadCount") long busyThreadCount) { totalExecutedTasksCount.increment(); - currentThreadsBusy.decrement(); + currentThreadsBusy.setCount(busyThreadCount); + } + + @ProbeListener("glassfish:kernel:thread-pool:setCurrentThreadCountEvent") + public void setCurrentThreadCountEvent( + @ProbeParam("monitoringId") String monitoringId, + @ProbeParam("threadPoolName") String threadPoolName, + @ProbeParam("currentThreadCount") int currentThreadCount) { + this.currentThreadCount.setCount(currentThreadCount); } } diff --git a/nucleus/core/kernel/src/main/java/org/glassfish/runnablejar/UberMain.java b/nucleus/core/kernel/src/main/java/org/glassfish/runnablejar/UberMain.java index 7e78ffaf99c..675342b9240 100644 --- a/nucleus/core/kernel/src/main/java/org/glassfish/runnablejar/UberMain.java +++ b/nucleus/core/kernel/src/main/java/org/glassfish/runnablejar/UberMain.java @@ -40,6 +40,7 @@ import org.glassfish.runnablejar.commandline.CommandLineParser; import static java.lang.System.exit; +import static java.util.logging.Level.CONFIG; import static java.util.logging.Level.FINE; import static java.util.logging.Level.INFO; import static java.util.logging.Level.SEVERE; @@ -176,7 +177,7 @@ private void runCommandPromptLoop() throws GlassFishException { } private void executeCommandFromString(String stringCommand) { - logger.log(FINE, () -> "Executing command: " + stringCommand); + logger.log(CONFIG, () -> "Executing command: " + stringCommand); // Split according to empty space but not if empty space is escaped by \ String[] split = stringCommand.split("(?--add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED --add-opens=java.naming/javax.naming.spi=org.glassfish.main.jdke --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED + --add-exports=java.base/jdk.internal.loader=ALL-UNNAMED diff --git a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/FlashlightLoggerInfo.java b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/FlashlightLoggerInfo.java index e261db84164..492a7faa018 100644 --- a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/FlashlightLoggerInfo.java +++ b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/FlashlightLoggerInfo.java @@ -132,11 +132,6 @@ public static Logger getLogger() { level = "WARNING") public static final String NO_ATTACH_API = LOGMSG_PREFIX + "-00510"; - @LogMessageInfo( - message = "Error while getting Instrumentation object from ProbeAgentMain", - level = "WARNING") - public static final String NO_ATTACH_GET = LOGMSG_PREFIX + "-00511"; - @LogMessageInfo( message = "DTrace is not available.", cause="This is caused if following are missing: \n1. JDK 7 is required to run DTrace\n2. glassfish-dtrace.jar value-add is required for DTrace", diff --git a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/cli/EnableMonitoring.java b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/cli/EnableMonitoring.java index 9d2bf7bc4b9..e7300b1dae0 100644 --- a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/cli/EnableMonitoring.java +++ b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/cli/EnableMonitoring.java @@ -126,7 +126,7 @@ public void execute(AdminCommandContext context) { if (!AgentAttacher.attachAgent(pidInt, options)) { ActionReport.MessagePart part = report.getTopMessagePart().addChild(); part.setMessage(localStrings.getLocalString("attach.agent.exception", - "Can't attach the agent to the JVM.")); + "Can't attach the monitoring agent to the JVM. Restart with the flashlight-agent.jar attached by the -javaagent command line parameter")); report.setActionExitCode(ActionReport.ExitCode.FAILURE); return; } diff --git a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/AgentAttacher.java b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/AgentAttacher.java index 8cf08ae96b5..d080c203a08 100644 --- a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/AgentAttacher.java +++ b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/AgentAttacher.java @@ -13,26 +13,63 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 */ - package org.glassfish.flashlight.impl.client; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Method; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.glassfish.flashlight.FlashlightLoggerInfo; + + /** * created May 26, 2011 + * * @author Byron Nevins */ public final class AgentAttacher { + + private static final String AGENT_CLASSNAME = "org.glassfish.flashlight.agent.ProbeAgentMain"; + + private static final Logger logger = FlashlightLoggerInfo.getLogger(); + + public static Optional getInstrumentation() { + try { + Class agentMainClass = null; + ClassLoader classLoader = ClassLoader.getSystemClassLoader(); + + try { + agentMainClass = classLoader.loadClass(AGENT_CLASSNAME); + } catch (Throwable t) { + // need throwable, not Exception - it may throw an Error! + // try one more time after attempting to attach. + AgentAttacher.attachAgent(); + // might throw + agentMainClass = classLoader.loadClass(AGENT_CLASSNAME); + } + + Method mthd = agentMainClass.getMethod("getInstrumentation", null); + return Optional.ofNullable((Instrumentation) mthd.invoke(null, null)); + } catch (Throwable throwable) { + logger.log(Level.WARNING, "Error while getting Instrumentation object from ProbeAgentMain", throwable); + return Optional.empty(); + } + } + public synchronized static boolean canAttach() { return canAttach; } public synchronized static boolean isAttached() { try { - if (!canAttach) + if (!canAttach) { return false; + } return AgentAttacherInternal.isAttached(); - } - catch (Throwable t) { + } catch (Throwable t) { return false; } } @@ -40,24 +77,24 @@ public synchronized static boolean isAttached() { public synchronized static boolean attachAgent() { try { - if (!canAttach) + if (!canAttach) { return false; + } return attachAgent(-1, ""); - } - catch (Throwable t) { + } catch (Throwable t) { return false; } } public synchronized static boolean attachAgent(int pid, String options) { try { - if (!canAttach) + if (!canAttach) { return false; + } return AgentAttacherInternal.attachAgent(pid, options); - } - catch (Throwable t) { + } catch (Throwable t) { return false; } } @@ -71,8 +108,7 @@ public synchronized static boolean attachAgent(int pid, String options) { // this is a distinct possibility in embedded mode. AgentAttacherInternal.isAttached(); b = true; - } - catch (Throwable t) { + } catch (Throwable t) { b = false; } canAttach = b; diff --git a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/AgentAttacherInternal.java b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/AgentAttacherInternal.java index 56cdee4fd59..2b395d0e068 100644 --- a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/AgentAttacherInternal.java +++ b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/AgentAttacherInternal.java @@ -55,6 +55,12 @@ static boolean attachAgent(long pid, String options) { } if (pid < 0) { + + if (AgentAttacher.getInstrumentation().isPresent()) { + isAttached = true; + return true; + } + pid = ProcessHandle.current().pid(); } diff --git a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/ProbeProviderClassFileTransformer.java b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/ProbeProviderClassFileTransformer.java index ba5c9383c66..cbb1c219e23 100644 --- a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/ProbeProviderClassFileTransformer.java +++ b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/impl/client/ProbeProviderClassFileTransformer.java @@ -48,7 +48,6 @@ import static org.glassfish.embeddable.GlassFishVariable.INSTALL_ROOT; import static org.glassfish.flashlight.FlashlightLoggerInfo.NO_ATTACH_API; -import static org.glassfish.flashlight.FlashlightLoggerInfo.NO_ATTACH_GET; import static org.glassfish.flashlight.FlashlightLoggerInfo.REGISTRATION_ERROR; import static org.glassfish.flashlight.FlashlightLoggerInfo.RETRANSFORMATION_ERROR; import static org.glassfish.flashlight.FlashlightLoggerInfo.WRITE_ERROR; @@ -82,7 +81,6 @@ public class ProbeProviderClassFileTransformer implements ClassFileTransformer { private static final Instrumentation instrumentation; private static boolean _debug = Boolean.parseBoolean(Utility.getEnvOrProp("AS_DEBUG")); private static boolean emittedAttachUnavailableMessageAlready = false; - private static final String AGENT_CLASSNAME = "org.glassfish.flashlight.agent.ProbeAgentMain"; private static final Logger logger = FlashlightLoggerInfo.getLogger(); private ProbeProviderClassFileTransformer(Class providerClass) { @@ -494,28 +492,7 @@ private Method getMethod(FlashlightProbe probe) throws NoSuchMethodException { if (AgentAttacher.canAttach()) { canAttach = true; - try { - ClassLoader classLoader = ClassLoader.getSystemClassLoader(); - - try { - agentMainClass = classLoader.loadClass(AGENT_CLASSNAME); - } - catch (Throwable t) { - // need throwable, not Exception - it may throw an Error! - // try one more time after attempting to attach. - AgentAttacher.attachAgent(); - // might throw - agentMainClass = classLoader.loadClass(AGENT_CLASSNAME); - } - - Method mthd = agentMainClass.getMethod("getInstrumentation", null); - nonFinalInstrumentation = (Instrumentation) mthd.invoke(null, null); - } - catch (Throwable t) { - nonFinalInstrumentation = null; - // save it for nice neat message code below - throwable = t; - } + nonFinalInstrumentation = AgentAttacher.getInstrumentation().orElse(null); } // set the final instrumentation = nonFinalInstrumentation; @@ -525,7 +502,7 @@ private Method getMethod(FlashlightProbe probe) throws NoSuchMethodException { } else if (instrumentation != null) { Log.info("yes.attach.api", instrumentation); } else { - logger.log(Level.WARNING, NO_ATTACH_GET, throwable); + logger.log(Level.WARNING, "Could not attach agent"); } } } diff --git a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/provider/FlashlightProbe.java b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/provider/FlashlightProbe.java index b13a57fb185..5dc37c30ac8 100644 --- a/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/provider/FlashlightProbe.java +++ b/nucleus/flashlight/framework/src/main/java/org/glassfish/flashlight/provider/FlashlightProbe.java @@ -146,10 +146,9 @@ public void fireProbe(Object[] params) { parent.fireProbe(params); } - int sz = invokerList.size(); + List invokers = new ArrayList<>(invokerList); - for (int i=0; i fireProbeBefore(Object[] params) { probeInvokeStates.addAll(parentStates); } - int sz = invokerList.size(); + List invokers = new ArrayList<>(invokerList); - for (int i=0; i invokers = new ArrayList<>(invokerList); int stateIndex = -1; - for (int i=0; i= 0) - invoker.invokeOnException(states.get(stateIndex).getState(), exceptionValue); + statefulInvoker.invokeOnException(states.get(stateIndex).getState(), exceptionValue); } } } diff --git a/nucleus/grizzly/config/src/main/java/org/glassfish/grizzly/config/GrizzlyListener.java b/nucleus/grizzly/config/src/main/java/org/glassfish/grizzly/config/GrizzlyListener.java index e3ffd8474ad..0b969f08e0b 100644 --- a/nucleus/grizzly/config/src/main/java/org/glassfish/grizzly/config/GrizzlyListener.java +++ b/nucleus/grizzly/config/src/main/java/org/glassfish/grizzly/config/GrizzlyListener.java @@ -43,6 +43,10 @@ public interface GrizzlyListener { void stop() throws IOException; + default void stop(boolean lastOne) throws IOException { + stop(); + } + void destroy(); String getName();