Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions core/src/main/java/org/apache/cxf/message/MessageUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.apache.cxf.message;

import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.util.Optional;
import java.util.logging.Logger;

Expand Down Expand Up @@ -205,4 +206,54 @@ public static Optional<Method> getTargetMethod(Message m) {
}
return Optional.ofNullable(method);
}

/**
* Gets the response code from the message and tries to deduct one if it
* is not set yet.
* @param message message to get response code from
* @return response code (or deducted value assuming success)
*/
public static int getReponseCodeFromMessage(Message message) {
Integer i = (Integer)message.get(Message.RESPONSE_CODE);
if (i != null) {
return i.intValue();
}
int code = hasNoResponseContent(message) ? HttpURLConnection.HTTP_ACCEPTED : HttpURLConnection.HTTP_OK;
// put the code in the message so that others can get it
message.put(Message.RESPONSE_CODE, code);
return code;
}

/**
* Determines if the current message has no response content.
* The message has no response content if either:
* - the request is oneway and the current message is no partial
* response or an empty partial response.
* - the request is not oneway but the current message is an empty partial
* response.
* @param message
* @return
*/
public static boolean hasNoResponseContent(Message message) {
final boolean ow = isOneWay(message);
final boolean pr = MessageUtils.isPartialResponse(message);
final boolean epr = MessageUtils.isEmptyPartialResponse(message);

//REVISIT may need to provide an option to choose other behavior?
// old behavior not suppressing any responses => ow && !pr
// suppress empty responses for oneway calls => ow && (!pr || epr)
// suppress additionally empty responses for decoupled twoway calls =>
return (ow && !pr) || epr;
}

/**
* Checks if the message is oneway or not
* @param message the message under consideration
* @return true if the message has been marked as oneway
*/
public static boolean isOneWay(Message message) {
final Exchange ex = message.getExchange();
return ex != null && ex.isOneWay();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageUtils;

import io.micrometer.core.instrument.Tag;

Expand Down Expand Up @@ -65,10 +66,8 @@ public Tag method(Message request) {

public Tag status(Message response) {
return ofNullable(response)
.map(e -> e.get(Message.RESPONSE_CODE))
.filter(e -> e instanceof Integer)
.map(e -> (Integer) e)
.map(String::valueOf)
.map(e -> MessageUtils.getReponseCodeFromMessage(response))
.map(code -> Integer.toString(code))
.map(status -> Tag.of("status", status))
.orElse(STATUS_UNKNOWN);
}
Expand All @@ -90,11 +89,8 @@ public Tag exception(Class<?> exceptionClass) {
}

public Tag outcome(Message response) {
Optional<Integer> statusCode =
ofNullable(response)
.map(e -> e.get(Message.RESPONSE_CODE))
.filter(e -> e instanceof Integer)
.map(e -> (Integer) e);
Optional<Integer> statusCode = ofNullable(response)
.map(e -> MessageUtils.getReponseCodeFromMessage(response));
if (!statusCode.isPresent()) {
return OUTCOME_UNKNOWN;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,17 @@ public class JaxwsFaultCodeProvider {

public String getFaultCode(Exchange ex, boolean client) {
FaultMode fm = ex.get(FaultMode.class);
// We check OutFaultMessage/InFaultMessage only because some features propagate the
// fault mode using InMessage/OutMessage (which may not end-up with a fault), for
// example check MAPAggregatorImpl.
if (client) {
if (fm == null && ex.getInFaultMessage() != null) {
fm = ex.getInFaultMessage().get(FaultMode.class);
}
if (fm == null && ex.getOutMessage() != null) {
fm = ex.getOutMessage().get(FaultMode.class);
}
} else {
if (fm == null && ex.getOutFaultMessage() != null) {
fm = ex.getOutFaultMessage().get(FaultMode.class);
}
if (fm == null && ex.getInMessage() != null) {
Copy link
Member Author

@reta reta Nov 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shark300 sorry for pinging, I've removed In Message (and Out Message) from the fault code resolution. It seems like it is used by some features and interceptors, however the fault logic only relies on In Fault / Out Fault. Do you have any concerns with this change? Thank you.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, thank you for that :)
"Adjust the StandardTags.status() method to treat null values as 200" Is it true in this case? I mean what is the HTTP status code in this case? As I see, the logic should be similar to this one: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/http/Outcome.java

Could you add a new integration test for this case?

Copy link
Member Author

@reta reta Nov 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @shark300 , if for any reasons the response code is not set, we reuse the logic from AbstractHttpDestination (https://github.com/apache/cxf/pull/726/files#diff-d9de014df7f37e23ad17d9091884dd0edcd9203b26d6f1a4c8d55b9f802ab957R216), in the nutshell it is 200 or 202 (partial response), not ideal but at least consistent.

I don't have an integration test for this particular case (it is difficult to verify that indeed response code was not set on server side, since client will always get it), but I have unit tests for it.

fm = ex.getInMessage().get(FaultMode.class);
}
}

if (fm == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,26 +192,27 @@ public void testStatusReturnWithUnknownWhenResponseIsNull() {
}

@Test
public void testStatusReturnWithUnknownWhenStatusIsNull() {
public void testStatusReturnWith200WhenResponseCodeIsNotSet() {
// given

// when
Tag actual = underTest.status(response);

// then
assertThat(actual, is(Tag.of(STATUS_METRIC_NAME, "UNKNOWN")));
assertThat(actual, is(Tag.of(STATUS_METRIC_NAME, "200")));
}

@Test
public void testStatusReturnWithUnknownWhenResponseCodeIsNotInteger() {
public void testStatusReturnWith202WhenResponseCodeIsNullAndResponseIsPartial() {
// given
doReturn("").when(request).get(Message.RESPONSE_CODE);
doReturn(null).when(request).get(Message.RESPONSE_CODE);
doReturn(true).when(request).get(Message.EMPTY_PARTIAL_RESPONSE_MESSAGE);

// when
Tag actual = underTest.status(request);

// then
assertThat(actual, is(Tag.of(STATUS_METRIC_NAME, "UNKNOWN")));
assertThat(actual, is(Tag.of(STATUS_METRIC_NAME, "202")));
}

@Test
Expand Down Expand Up @@ -327,18 +328,6 @@ public void testOutcomeReturnWithUnknownWhenResponseCodeIsNull() {
Tag actual = underTest.outcome(response);

// then
assertThat(actual, is(Tag.of(OUTCOME_METRIC_NAME, "UNKNOWN")));
}

@Test
public void testOutcomeReturnWithUnknownWhenMethodIsNotInteger() {
// given
doReturn(new Object()).when(response).get(Message.BASE_PATH);

// when
Tag actual = underTest.outcome(response);

// then
assertThat(actual, is(Tag.of(OUTCOME_METRIC_NAME, "UNKNOWN")));
assertThat(actual, is(Tag.of(OUTCOME_METRIC_NAME, "SUCCESS")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,21 @@ public void testFaultModeIsNotPresentButOutFaultModeIsPresentThenShouldReturnTha
// then
assertThat(actual, equalTo(RUNTIME_FAULT_STRING));
}

@Test
public void testFaultModeIsNotPresentButInFaultModeIsPresentThenShouldReturnThat() {
// given
doReturn(null).when(ex).get(FaultMode.class);
doReturn(message).when(ex).getInFaultMessage();
doReturn(RUNTIME_FAULT).when(message).get(FaultMode.class);

// when
String actual = underTest.getFaultCode(ex, true);

// then
assertThat(actual, equalTo(RUNTIME_FAULT_STRING));
}


@Test
public void testFaultModeIsNotPresentButOutFaultModeIsMissingThenShouldReturnNull() {
Expand All @@ -92,7 +107,7 @@ public void testFaultModeIsNotPresentButOutFaultModeIsMissingThenShouldReturnNul
}

@Test
public void testNeitherFaultModeNorOutFaultModePresentsThenShouldReturnInMessagesFaultMode() {
public void testNeitherFaultModeNorOutFaultModePresentsThenShouldNotReturnInMessageFaultMode() {
// given
doReturn(null).when(ex).get(FaultMode.class);
doReturn(null).when(ex).getOutFaultMessage();
Expand All @@ -103,7 +118,22 @@ public void testNeitherFaultModeNorOutFaultModePresentsThenShouldReturnInMessage
String actual = underTest.getFaultCode(ex, false);

// then
assertThat(actual, equalTo(RUNTIME_FAULT_STRING));
assertThat(actual, is(nullValue()));
}

@Test
public void testNeitherFaultModeNorOutFaultModePresentsThenShouldNotReturnOutMessageFaultMode() {
// given
doReturn(null).when(ex).get(FaultMode.class);
doReturn(null).when(ex).getInFaultMessage();
doReturn(message).when(ex).getOutMessage();
doReturn(RUNTIME_FAULT).when(message).get(FaultMode.class);

// when
String actual = underTest.getFaultCode(ex, true);

// then
assertThat(actual, is(nullValue()));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.ServerSocket;
import java.net.URL;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -235,8 +234,7 @@ public String getUserName() {
* @return true iff the message has been marked as oneway
*/
protected final boolean isOneWay(Message message) {
Exchange ex = message.getExchange();
return ex != null && ex.isOneWay();
return MessageUtils.isOneWay(message);
}

public void invoke(final ServletConfig config,
Expand Down Expand Up @@ -632,7 +630,7 @@ protected OutputStream flushHeaders(Message outMessage, boolean getStream) throw

HttpServletResponse response = getHttpResponseFromMessage(outMessage);

int responseCode = getReponseCodeFromMessage(outMessage);
int responseCode = MessageUtils.getReponseCodeFromMessage(outMessage);
if (responseCode >= 300) {
String ec = (String)outMessage.get(Message.ERROR_MESSAGE);
if (!StringUtils.isEmpty(ec)) {
Expand All @@ -645,7 +643,7 @@ protected OutputStream flushHeaders(Message outMessage, boolean getStream) throw

outMessage.put(RESPONSE_HEADERS_COPIED, "true");

if (hasNoResponseContent(outMessage)) {
if (MessageUtils.hasNoResponseContent(outMessage)) {
response.setContentLength(0);
response.flushBuffer();
closeResponseOutputStream(response);
Expand All @@ -669,38 +667,6 @@ private void closeResponseOutputStream(HttpServletResponse response) throws IOEx
}
}

private int getReponseCodeFromMessage(Message message) {
Integer i = (Integer)message.get(Message.RESPONSE_CODE);
if (i != null) {
return i.intValue();
}
int code = hasNoResponseContent(message) ? HttpURLConnection.HTTP_ACCEPTED : HttpURLConnection.HTTP_OK;
// put the code in the message so that others can get it
message.put(Message.RESPONSE_CODE, code);
return code;
}

/**
* Determines if the current message has no response content.
* The message has no response content if either:
* - the request is oneway and the current message is no partial
* response or an empty partial response.
* - the request is not oneway but the current message is an empty partial
* response.
* @param message
* @return
*/
private boolean hasNoResponseContent(Message message) {
final boolean ow = isOneWay(message);
final boolean pr = MessageUtils.isPartialResponse(message);
final boolean epr = MessageUtils.isEmptyPartialResponse(message);

//REVISIT may need to provide an option to choose other behavior?
// old behavior not suppressing any responses => ow && !pr
// suppress empty responses for oneway calls => ow && (!pr || epr)
// suppress additionally empty responses for decoupled twoway calls =>
return (ow && !pr) || epr;
}

private HttpServletResponse getHttpResponseFromMessage(Message message) throws IOException {
Object responseObj = message.get(HTTP_RESPONSE);
Expand Down