Skip to content

Commit 161d0b1

Browse files
AXIS2-6099 add some unit tests and better Wildfly dep verification
1 parent 436a455 commit 161d0b1

File tree

3 files changed

+182
-71
lines changed

3 files changed

+182
-71
lines changed

modules/transport-h2/src/main/java/org/apache/axis2/transport/h2/integration/moshi/UndertowAxis2BufferIntegration.java

Lines changed: 156 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,14 @@
4040
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
4141
import org.apache.hc.core5.http2.HttpVersionPolicy;
4242
import org.apache.hc.core5.http2.config.H2Config;
43+
import org.xnio.Pool;
4344
import org.xnio.XnioWorker;
4445

4546
import com.squareup.moshi.JsonReader;
4647

4748
import okio.BufferedSource;
4849
import okio.Okio;
4950

50-
/**
51-
* Simple Pool interface for buffer management compatibility.
52-
*/
53-
interface Pool<T> {
54-
T allocate();
55-
void free(T item);
56-
int getAllocatedObjectCount();
57-
}
5851

5952
/**
6053
* Moshi-specific Axis2 integration with Undertow shared buffer pools using Axis2 architecture patterns.
@@ -75,24 +68,161 @@ public class UndertowAxis2BufferIntegration {
7568
private static final Log log = LogFactory.getLog(UndertowAxis2BufferIntegration.class);
7669

7770
/**
78-
* Initialize Moshi-specific integration with Undertow servlet context.
79-
*
80-
* @param servletContext The servlet context providing access to Undertow's XNIO worker and buffer pool
71+
* Cached WildFly integration singleton - initialized once, reused across all HTTP requests.
72+
* This ensures zero performance overhead in high-volume enterprise applications.
73+
*/
74+
private static volatile WildFlyResourceCache wildflyCache = null;
75+
private static final Object initLock = new Object();
76+
77+
/**
78+
* Cached WildFly resources discovered once during first servlet context access.
79+
*/
80+
private static class WildFlyResourceCache {
81+
final XnioWorker xnioWorker;
82+
final Pool<ByteBuffer> sharedBufferPool;
83+
final boolean integrationAvailable;
84+
final String discoveryLog;
85+
86+
WildFlyResourceCache(ServletContext servletContext) {
87+
StringBuilder discovery = new StringBuilder("WildFly resource discovery: ");
88+
XnioWorker worker = null;
89+
Pool<ByteBuffer> pool = null;
90+
91+
// Try multiple possible WildFly attribute names (test-compatible first)
92+
String[] workerNames = {
93+
"io.undertow.servlet.XnioWorker", // Test compatibility - current expected name
94+
"io.undertow.xnio.worker", // Alternative WildFly name
95+
"org.wildfly.undertow.worker", // WildFly-specific name
96+
"undertow.xnio.worker" // Generic name
97+
};
98+
99+
String[] poolNames = {
100+
"io.undertow.servlet.BufferPool", // Test compatibility - current expected name
101+
"io.undertow.buffer-pool", // Alternative WildFly name
102+
"org.wildfly.undertow.buffer.pool", // WildFly-specific name
103+
"undertow.buffer.pool" // Generic name
104+
};
105+
106+
// Search for XNIO Worker
107+
for (String name : workerNames) {
108+
Object attr = servletContext.getAttribute(name);
109+
if (attr instanceof XnioWorker) {
110+
worker = (XnioWorker) attr;
111+
discovery.append("Found XnioWorker at '").append(name).append("', ");
112+
break;
113+
}
114+
}
115+
116+
// Search for Buffer Pool
117+
for (String name : poolNames) {
118+
Object attr = servletContext.getAttribute(name);
119+
if (attr instanceof Pool) {
120+
try {
121+
@SuppressWarnings("unchecked")
122+
Pool<ByteBuffer> bufferPool = (Pool<ByteBuffer>) attr;
123+
pool = bufferPool;
124+
discovery.append("Found BufferPool at '").append(name).append("', ");
125+
break;
126+
} catch (ClassCastException e) {
127+
// Not a ByteBuffer pool, continue searching
128+
}
129+
}
130+
}
131+
132+
this.xnioWorker = worker;
133+
this.sharedBufferPool = pool;
134+
this.integrationAvailable = (worker != null && pool != null);
135+
this.discoveryLog = discovery.toString();
136+
137+
// Log discovery results once
138+
if (integrationAvailable) {
139+
log.info("WildFly integration available: " + discoveryLog);
140+
} else {
141+
log.debug("WildFly integration not available: " + discoveryLog +
142+
"Worker=" + (worker != null) + ", Pool=" + (pool != null));
143+
144+
// Debug: enumerate all servlet context attributes to help discover correct names
145+
if (log.isDebugEnabled()) {
146+
enumerateServletContextAttributes(servletContext);
147+
}
148+
}
149+
}
150+
151+
/**
152+
* Debug helper: enumerate all servlet context attributes to discover WildFly's actual attribute names.
153+
* Only called when debug logging is enabled and integration fails.
154+
*/
155+
private void enumerateServletContextAttributes(ServletContext servletContext) {
156+
try {
157+
java.util.Enumeration<String> attributeNames = servletContext.getAttributeNames();
158+
StringBuilder allAttrs = new StringBuilder("All servlet context attributes: ");
159+
160+
while (attributeNames.hasMoreElements()) {
161+
String name = attributeNames.nextElement();
162+
Object value = servletContext.getAttribute(name);
163+
String className = (value != null) ? value.getClass().getSimpleName() : "null";
164+
allAttrs.append(name).append("=").append(className).append(", ");
165+
}
166+
167+
log.debug(allAttrs.toString());
168+
} catch (Exception e) {
169+
log.debug("Failed to enumerate servlet context attributes: " + e.getMessage());
170+
}
171+
}
172+
}
173+
174+
/**
175+
* Check if WildFly integration is available for this instance.
176+
* @return true if both XNIO worker and buffer pool are available for this instance
177+
*/
178+
public boolean isIntegrationAvailable() {
179+
return xnioWorker != null && sharedBufferPool != null;
180+
}
181+
182+
/**
183+
* Get integration discovery information for monitoring/debugging.
184+
* @return discovery log string, or null if not yet discovered
185+
*/
186+
public static String getDiscoveryInfo() {
187+
WildFlyResourceCache cache = wildflyCache;
188+
return cache != null ? cache.discoveryLog : "Not yet initialized";
189+
}
190+
191+
/**
192+
* Clear the static cache - useful for testing environments.
193+
* Package-private for use in test classes.
194+
*/
195+
static void clearCache() {
196+
wildflyCache = null;
197+
}
198+
199+
/**
200+
* Initialize Moshi-specific integration with cached WildFly resources.
201+
* First call discovers and caches resources, subsequent calls reuse cached values.
202+
* @param servletContext The servlet context (used only for first-time discovery)
81203
*/
82204
public UndertowAxis2BufferIntegration(ServletContext servletContext) {
83-
// Access Undertow's XNIO worker and buffer pool
84-
this.xnioWorker = (XnioWorker) servletContext
85-
.getAttribute("io.undertow.servlet.XnioWorker");
86-
this.sharedBufferPool = (Pool<ByteBuffer>) servletContext
87-
.getAttribute("io.undertow.servlet.BufferPool");
88-
89-
if (xnioWorker == null) {
90-
log.warn("XNIO Worker not found in servlet context - Undertow integration may be limited");
205+
// Lazy initialization with double-checked locking for thread safety
206+
if (wildflyCache == null) {
207+
synchronized (initLock) {
208+
if (wildflyCache == null) {
209+
wildflyCache = new WildFlyResourceCache(servletContext);
210+
}
211+
}
91212
}
92-
if (sharedBufferPool == null) {
93-
log.warn("Shared buffer pool not found in servlet context - using default buffer management");
94-
} else {
95-
log.info("Successfully integrated Moshi-based Axis2 with Undertow shared buffer pool");
213+
214+
// Use cached resources (zero lookup overhead after first initialization)
215+
this.xnioWorker = wildflyCache.xnioWorker;
216+
this.sharedBufferPool = wildflyCache.sharedBufferPool;
217+
218+
// Emit warnings only if this is a fresh discovery attempt
219+
if (!wildflyCache.integrationAvailable) {
220+
if (xnioWorker == null) {
221+
log.warn("XNIO Worker not found in servlet context - Undertow integration may be limited");
222+
}
223+
if (sharedBufferPool == null) {
224+
log.warn("Shared buffer pool not found in servlet context - using default buffer management");
225+
}
96226
}
97227
}
98228

@@ -148,7 +278,7 @@ protected CloseableHttpAsyncClient createHTTP2Client(long payloadSize) {
148278
private int getSharedBufferSize() {
149279
if (sharedBufferPool != null) {
150280
// Align with Undertow's buffer configuration and Axis2's needs
151-
return sharedBufferPool.getAllocatedObjectCount() > 0 ? 4096 : 2048;
281+
return 4096; // Use 4KB buffers when shared pool is available
152282
} else {
153283
// Fallback to default Axis2 buffer size optimized for Moshi
154284
return 65536; // 64KB default for large JSON payloads
@@ -215,8 +345,7 @@ private BufferedSource createOptimizedBufferedSource(InputStream inputStream) {
215345
// Create BufferedSource with shared buffer pool awareness for Moshi optimization
216346
// Note: Okio uses its own buffer management, but we coordinate with shared pool metrics
217347
if (sharedBufferPool != null && log.isDebugEnabled()) {
218-
log.debug("Creating Moshi-optimized BufferedSource with shared buffer pool awareness - " +
219-
"allocated buffers: " + sharedBufferPool.getAllocatedObjectCount());
348+
log.debug("Creating Moshi-optimized BufferedSource with shared buffer pool awareness");
220349
}
221350
return Okio.buffer(Okio.source(inputStream));
222351
}
@@ -240,14 +369,6 @@ public Pool<ByteBuffer> getSharedBufferPool() {
240369
return sharedBufferPool;
241370
}
242371

243-
/**
244-
* Check if Undertow integration is fully available.
245-
*
246-
* @return true if both XNIO worker and buffer pool are available
247-
*/
248-
public boolean isIntegrationAvailable() {
249-
return xnioWorker != null && sharedBufferPool != null;
250-
}
251372

252373
/**
253374
* Get integration status for monitoring and debugging.
@@ -261,7 +382,7 @@ public String getIntegrationStatus() {
261382
if (isIntegrationAvailable()) {
262383
status.append("FULL - XNIO Worker and Buffer Pool available");
263384
if (sharedBufferPool != null) {
264-
status.append(", Allocated Buffers: ").append(sharedBufferPool.getAllocatedObjectCount());
385+
status.append(", Buffer Pool: available");
265386
}
266387
} else if (xnioWorker != null) {
267388
status.append("PARTIAL - XNIO Worker available, Buffer Pool missing");

modules/transport-h2/src/test/java/org/apache/axis2/transport/h2/integration/moshi/MemoryConstraintValidationTest.java

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import io.undertow.server.HttpServerExchange;
4646
import io.undertow.util.HeaderMap;
4747
import io.undertow.util.Headers;
48+
import org.xnio.Pool;
4849

4950
/**
5051
* Memory Constraint Validation Test Suite for WildFly 32 + Axis2 HTTP/2 Integration.
@@ -84,6 +85,9 @@ public class MemoryConstraintValidationTest {
8485
public void setUp() throws Exception {
8586
MockitoAnnotations.openMocks(this);
8687

88+
// Clear static cache to ensure test isolation
89+
UndertowAxis2BufferIntegration.clearCache();
90+
8791
// Initialize memory coordination
8892
memoryCoordinator = new HTTP2MemoryCoordinator();
8993

@@ -488,27 +492,11 @@ public void testLongRunningOperationMemoryStability() throws Exception {
488492

489493
// Helper methods and classes
490494

495+
@SuppressWarnings("unchecked")
491496
private Pool<ByteBuffer> createMockBufferPoolWithTracking() {
492-
return new Pool<ByteBuffer>() {
493-
private int allocatedCount = 0;
494-
private int maxAllocated = 10;
495-
496-
@Override
497-
public ByteBuffer allocate() {
498-
allocatedCount++;
499-
return ByteBuffer.allocate(8192); // 8KB buffers
500-
}
501-
502-
@Override
503-
public void free(ByteBuffer item) {
504-
allocatedCount = Math.max(0, allocatedCount - 1);
505-
}
506-
507-
@Override
508-
public int getAllocatedObjectCount() {
509-
return Math.min(allocatedCount, maxAllocated);
510-
}
511-
};
497+
Pool<ByteBuffer> pool = mock(Pool.class);
498+
// Mock the pool - using Mockito to avoid XNIO interface complexity in tests
499+
return pool;
512500
}
513501

514502
private String generateMemoryTestPayload(long targetSizeBytes) {

modules/transport-h2/src/test/java/org/apache/axis2/transport/h2/integration/moshi/WildFlyAxis2CooperativeIntegrationTest.java

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.junit.Test;
4141
import org.mockito.Mock;
4242
import org.mockito.MockitoAnnotations;
43+
import org.xnio.Pool;
4344
import org.xnio.XnioWorker;
4445

4546
import io.undertow.server.HttpServerExchange;
@@ -80,6 +81,9 @@ public class WildFlyAxis2CooperativeIntegrationTest {
8081
public void setUp() throws Exception {
8182
MockitoAnnotations.openMocks(this);
8283

84+
// Clear static cache to ensure test isolation
85+
UndertowAxis2BufferIntegration.clearCache();
86+
8387
// Mock WildFly buffer pool (key integration point)
8488
mockBufferPool = createMockBufferPool();
8589

@@ -310,10 +314,20 @@ public void testHTTP2StreamPrioritizationWithWildFly() throws Exception {
310314
*/
311315
@Test
312316
public void testWildFlyIntegrationFallback() {
317+
// Clear cache to ensure this test has clean state
318+
UndertowAxis2BufferIntegration.clearCache();
319+
313320
// Test integration without WildFly components
314321
ServletContext limitedContext = mock(ServletContext.class);
322+
// Mock all possible attribute names to return null
315323
when(limitedContext.getAttribute("io.undertow.servlet.XnioWorker")).thenReturn(null);
316324
when(limitedContext.getAttribute("io.undertow.servlet.BufferPool")).thenReturn(null);
325+
when(limitedContext.getAttribute("io.undertow.xnio.worker")).thenReturn(null);
326+
when(limitedContext.getAttribute("io.undertow.buffer-pool")).thenReturn(null);
327+
when(limitedContext.getAttribute("org.wildfly.undertow.worker")).thenReturn(null);
328+
when(limitedContext.getAttribute("org.wildfly.undertow.buffer.pool")).thenReturn(null);
329+
when(limitedContext.getAttribute("undertow.xnio.worker")).thenReturn(null);
330+
when(limitedContext.getAttribute("undertow.buffer.pool")).thenReturn(null);
317331

318332
UndertowAxis2BufferIntegration limitedIntegration = new UndertowAxis2BufferIntegration(limitedContext);
319333

@@ -331,23 +345,11 @@ public void testWildFlyIntegrationFallback() {
331345

332346
// Helper methods
333347

348+
@SuppressWarnings("unchecked")
334349
private Pool<ByteBuffer> createMockBufferPool() {
335-
return new Pool<ByteBuffer>() {
336-
@Override
337-
public ByteBuffer allocate() {
338-
return ByteBuffer.allocate(8192); // 8KB buffer
339-
}
340-
341-
@Override
342-
public void free(ByteBuffer item) {
343-
// Mock implementation
344-
}
345-
346-
@Override
347-
public int getAllocatedObjectCount() {
348-
return 10; // Mock active buffers
349-
}
350-
};
350+
Pool<ByteBuffer> pool = mock(Pool.class);
351+
// Mock the pool - using Mockito to avoid XNIO interface complexity in tests
352+
return pool;
351353
}
352354

353355
private String generateLargeJsonPayload(int sizeBytes) {

0 commit comments

Comments
 (0)