diff --git a/core/src/main/java/org/apache/cxf/endpoint/ClientImpl.java b/core/src/main/java/org/apache/cxf/endpoint/ClientImpl.java index 30ac1ba0b3a..3a0c5e99fc8 100644 --- a/core/src/main/java/org/apache/cxf/endpoint/ClientImpl.java +++ b/core/src/main/java/org/apache/cxf/endpoint/ClientImpl.java @@ -24,14 +24,7 @@ import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.WeakHashMap; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.logging.Level; @@ -107,6 +100,35 @@ public class ClientImpl protected Map responseContext = Collections.synchronizedMap(new WeakHashMap()); + /** + * Set of properties that should not be propagated into the ResponseContext from IN Message + */ + private static final Set RESPONSE_CONTEXT_EXCLUDED_IN_PROPERTIES = new HashSet<>( + List.of( + // remove the recursive reference if present + Message.INVOCATION_CONTEXT + ) + ); + + /** + * Method that a cxf submodule can call to add a property not to propagate from IN Message into the ResponseContext + * @param property to exclude + */ + public static void addResponseContextExcludedInProperty(String property) { + RESPONSE_CONTEXT_EXCLUDED_IN_PROPERTIES.add(property); + } + public static void addAllResponseContextExcludedInProperties(Set properties) { + RESPONSE_CONTEXT_EXCLUDED_IN_PROPERTIES.addAll(properties); + } + + /** + * Method to remove RESPONSE_CONTEXT_EXCLUDED_IN_PROPERTIES from ResponseContext Map + * @param context ResponseContext Map + */ + protected void filterResponseContextProperties(Map context) { + RESPONSE_CONTEXT_EXCLUDED_IN_PROPERTIES.forEach(context::remove); + } + protected Executor executor; public ClientImpl(Bus b, Endpoint e) { @@ -648,8 +670,7 @@ protected Object[] processResult(Message message, if (inMsg != null) { if (null != resContext) { resContext.putAll(inMsg); - // remove the recursive reference if present - resContext.remove(Message.INVOCATION_CONTEXT); + filterResponseContextProperties(resContext); setResponseContext(resContext); } resList = CastUtils.cast(inMsg.getContent(List.class)); diff --git a/core/src/test/java/org/apache/cxf/endpoint/ClientImplTest.java b/core/src/test/java/org/apache/cxf/endpoint/ClientImplTest.java new file mode 100644 index 00000000000..cce006b6f2b --- /dev/null +++ b/core/src/test/java/org/apache/cxf/endpoint/ClientImplTest.java @@ -0,0 +1,124 @@ +package org.apache.cxf.endpoint; + + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.cxf.BusFactory; +import org.apache.cxf.message.Message; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class ClientImplTest { + + private static class TestClientImpl extends ClientImpl { + + public TestClientImpl() { + super(BusFactory.newInstance().createBus(), null); + } + + void filter(Map context) { + filterResponseContextProperties(context); + } + } + + private final TestClientImpl testClientImpl = new TestClientImpl(); + + private static Set getExcludedProperties() throws Exception { + Field field = ClientImpl.class + .getDeclaredField("RESPONSE_CONTEXT_EXCLUDED_IN_PROPERTIES"); + + field.setAccessible(true); + + @SuppressWarnings("unchecked") + Set properties = (Set) field.get(null); + + return properties; + } + + private static Set defaultExcludedProperties; + + @BeforeClass + public static void initDefaults() throws Exception { + defaultExcludedProperties = + new HashSet<>(getExcludedProperties()); + } + + @Before + public void setUp() throws Exception { + Set properties = getExcludedProperties(); + + properties.clear(); + properties.addAll(defaultExcludedProperties); + } + + @Test + public void shouldFilterDefaultExcludedProperty() { + + Map context = new HashMap<>(); + context.put(Message.INVOCATION_CONTEXT, "invocation-context"); + context.put("property.to.keep", "value"); + + testClientImpl.filter(context); + + assertFalse(context.containsKey(Message.INVOCATION_CONTEXT)); + assertEquals("value", context.get("property.to.keep")); + } + + @Test + public void shouldFilterPropertyAddedWithAdd() { + String property = "my.custom.property"; + + ClientImpl.addResponseContextExcludedInProperty(property); + + Map context = new HashMap<>(); + context.put(property, "custom-value"); + context.put("property.to.keep", "value"); + + testClientImpl.filter(context); + + assertFalse(context.containsKey(property)); + assertEquals("value", context.get("property.to.keep")); + } + + @Test + public void shouldFilterPropertiesAddedWithAddAll() { + Set properties = Set.of( + "custom.property.1", + "custom.property.2" + ); + + ClientImpl.addAllResponseContextExcludedInProperties(properties); + + Map context = new HashMap<>(); + context.put("custom.property.1", "value1"); + context.put("custom.property.2", "value2"); + context.put("property.to.keep", "keep"); + + testClientImpl.filter(context); + + assertFalse(context.containsKey("custom.property.1")); + assertFalse(context.containsKey("custom.property.2")); + assertEquals("keep", context.get("property.to.keep")); + } + + @Test + public void shouldKeepPropertiesNotExcluded() { + + Map context = new HashMap<>(); + context.put("property.1", "value1"); + context.put("property.2", "value2"); + + testClientImpl.filter(context); + + assertEquals(2, context.size()); + assertEquals("value1", context.get("property.1")); + assertEquals("value2", context.get("property.2")); + } +} \ No newline at end of file diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java index 436bbf3d16d..149e0c170ff 100644 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java @@ -38,6 +38,8 @@ import org.apache.cxf.phase.Phase; import org.apache.cxf.phase.PhaseInterceptor; +import static org.apache.cxf.endpoint.ClientImpl.addResponseContextExcludedInProperty; + /** * */ @@ -71,6 +73,9 @@ public LoggingInInterceptor(PrintWriter writer) { public LoggingInInterceptor(LogEventSender sender) { super(Phase.PRE_INVOKE, sender); + + //Make sure that the LIVE_LOGGING_PROP won't be propagated into the ResponseContext from IN Messages + addResponseContextExcludedInProperty(LIVE_LOGGING_PROP); } public Collection> getAdditionalInterceptors() { diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/LoggingInInterceptorTest.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/LoggingInInterceptorTest.java index d7b75781504..c73416c7b39 100644 --- a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/LoggingInInterceptorTest.java +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/LoggingInInterceptorTest.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; @@ -29,6 +30,7 @@ import java.util.Map; import java.util.Set; +import org.apache.cxf.endpoint.ClientImpl; import org.apache.cxf.ext.logging.event.LogEvent; import org.apache.cxf.io.CachedOutputStream; import org.apache.cxf.message.ExchangeImpl; @@ -38,11 +40,13 @@ import org.junit.Before; import org.junit.Test; +import static org.apache.cxf.ext.logging.AbstractLoggingInterceptor.LIVE_LOGGING_PROP; import static org.apache.cxf.ext.logging.event.DefaultLogEventMapper.MASKED_HEADER_VALUE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class LoggingInInterceptorTest { private static final String TEST_HEADER_VALUE = "TestValue"; @@ -234,4 +238,17 @@ public void shouldLogMultipartPayloadNoHeaders() throws IOException { assertThat(event.getPayload(), equalToIgnoringCase(buf.toString())); } + + @Test + public void shouldAddResponseContextInExcludedProperty() throws NoSuchFieldException, IllegalAccessException { + Field field = ClientImpl.class + .getDeclaredField("RESPONSE_CONTEXT_EXCLUDED_IN_PROPERTIES"); + + field.setAccessible(true); + + @SuppressWarnings("unchecked") + Set actual = (Set) field.get(null); + + assertTrue(actual.contains(LIVE_LOGGING_PROP)); + } }