spring content negotiating force Mime-type in error case - spring

I ve got a spring service generating a pdf, if you call that service in the browser with .pdf path extension everything is working fine in success case.
However, in error case I would like to return a json error response as for all my other services.
Unfortuently, spring (Version 4.1.4) throws an exception org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
Even if I add an accept-header stating that pdf or json might be ok.
I performed following tests:
1. Request Call myService/doc.pdf with standard browser accept header
Result: Exception
2. Request Call myService/doc.pdf with specific accept header application/pdf; application/json
Result: Exception
3. Request Call myService/doc with standard browser accept header
json is returned succesfully in case of error
4. Request Call myService/doc with accept header application/pdf;application/json
json is returned succesfully in case of error
It seems to me that in case path extension is used by Spring to determine response mime-types, ONLY that Mime Type is allowed in response. However, I think it does not make any sense, to return a pdf document in case of error.
Does anybody know if I may overrule content negotiation in that case in my exception handler. Saying, well I know, that mime-type pdf is requested in url path extension, however, I Must return mime-type json.
Thanks a lot for your support in advance.
--
Volker
My content negotiation config:
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="true" />
<property name="favorParameter" value="false" />
<property name="defaultContentType" value="application/json"/>
<property name="mediaTypes" >
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>
My ErrorHandler:
#ExceptionHandler(MyControlException.class)
#ResponseBody
public ResponseEntity<ErrorModel> handleMyExceptions(final MyControlException ex, final HttpServletRequest request,
final HttpServletResponse res) {
ResponseEntity<ErrorModel> response;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
response = new ResponseEntity<ErrorModel>(ex.getError(), headers, HttpStatus.valueOf(ex.getError().getErrorCode()));
return response;
}
Exception Stack Trace:
o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Failed to invoke #ExceptionHandler method: public org.springframework.http.ResponseEntity<com.myapp.apps2Control.common.model.ErrorModel> com.myapp.apps2Control.rest.controller.BaseApps2MceController.handleMyExceptions(com.myapp.apps2Control.common.MyControlException,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:134) ~[spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:144) ~[spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71) ~[spring-web-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126) ~[spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.doResolveHandlerMethodException(ExceptionHandlerExceptionResolver.java:362) ~[spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver.doResolveException(AbstractHandlerMethodExceptionResolver.java:60) [spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:138) [spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.processHandlerException(DispatcherServlet.java:1167) [spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1004) [spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:955) [spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877) [spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) [spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857) [spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) [servlet-api.jar:na]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) [spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]

Related

Spring Server Error on unknown Accept header

I have a Spring REST project that uses spring boot. I have declared jackson-dataformat-xml as a dependency in maven to support xml when the accept header is application/xml (and it natively accepts application/json).
The problem I have is when i set the accept header to anything other than those two (ex application/dsfas or text/html, I get a server error with the following exception:
23:36:04.368 [http-nio-8082-exec-5] WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Failure in #ExceptionHandler protected org.springframework.http.ResponseEntity<java.lang.Object> com.mergg.common.web.RestResponseEntityExceptionHandler.handleNotFound(java.lang.RuntimeException,org.springframework.web.context.request.WebRequest)
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
Any idea on how to fix so that I can either ignore the accept header and return json or tell the client it was a bad request?
public class JsonContentNegotiation implements ContentNegotiationStrategy {
#Override
public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
System.out.println("This is your negotation Strategy");
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String headers = request.getHeader(HttpHeaders.ACCEPT);
List<MediaType> mediaTypes = MediaType.parseMediaTypes(headers);
if(headers.indexOf(MediaType.APPLICATION_JSON_VALUE)==-1){
mediaTypes.add(new MediaType("application","json"));
}
MediaType.sortBySpecificityAndQuality(mediaTypes);
return mediaTypes;
}
}
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="true"/>
<property name="ignoreAcceptHeader" value="true"/>
<property name="defaultContentTypeStrategy">
<bean class="demo2.ContentNegotation.JsonContentNegotiation"/>
</property>
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
otherwise use this in the controller to tell the client this is wrong header
#ExceptionHandler({HttpMediaTypeNotAcceptableException.class})
#ResponseBody
public String fix(Exception e){
System.out.println("do This");
return "Accept Header may be wrong";
}

Spring SAML: SAML message intended destination endpoint did not match recipient endpoint

I am getting 'Caused by: org.opensaml.xml.security.SecurityException: SAML message intended destination endpoint did not match recipient endpoint' exception while SSO between my app SP and client IdP.
Server log show the difference in schemas, see below:
Checking SAML message intended destination endpoint against receiver endpoint
2019-03-05 15:02:44.599 DEBUG [204 default task-41][BaseSAMLMessageDecoder] Intended message destination endpoint: https://my.app.com/app-gateway/saml/SSO
2019-03-05 15:02:44.599 DEBUG [204 default task-41][BaseSAMLMessageDecoder] Actual message receiver endpoint: http://my.app.com/app-gateway/saml/SSO
2019-03-05 15:02:44.600 ERROR [204 default task-41][BaseSAMLMessageDecoder] SAML message intended destination endpoint 'https://my.app.com/app-gateway/saml/SSO' did not match the recipient endpoint 'http://my.app.com/app-gateway/saml/SSO'
My application is running on STG on 2 instances with the LB in front, therefore I use SAMLContextProviderLB context provider instead of SAMLContextProviderImpl:
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB">
<property name="scheme" value="https"/>
<property name="serverName" value="my.app.com"/>
<property name="serverPort" value="443"/>
<property name="includeServerPortInRequestURL" value="false"/>
<property name="contextPath" value="/app-gateway"/>
</bean>
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="entityBaseURL" value="https://my.app.com/app-gateway1"/>
<property name="entityId" value="${cas.sso.entityId}"/>
<property name="includeDiscoveryExtension" value="false"/>
<property name="extendedMetadata" ref="extendedMetadata"/>
<property name="keyManager" ref="keyManager"/>
</bean>
</constructor-arg>
</bean>
In the source code of getActualReceiverEndpointURI the receiver endpoint URL is being taken from request httpRequest obj. Thus, I am trying to understand at which step that wrong URL http://my.app.com/app-gateway/saml/SSO was set to it. Can anyone explain me it?
protected String getActualReceiverEndpointURI(SAMLMessageContext messageContext) throws MessageDecodingException {
InTransport inTransport = messageContext.getInboundMessageTransport();
if (! (inTransport instanceof HttpServletRequestAdapter)) {
log.error("Message context InTransport instance was an unsupported type: {}",
inTransport.getClass().getName());
throw new MessageDecodingException("Message context InTransport instance was an unsupported type");
}
HttpServletRequest httpRequest = ((HttpServletRequestAdapter)inTransport).getWrappedRequest();
StringBuffer urlBuilder = httpRequest.getRequestURL();
return urlBuilder.toString();
}
You might want to check the following page :
https://developer.jboss.org/thread/240113
I had a similar issue, even with X-Forwarded-Proto properly set on the LB, the request was still interpreted in http only.
The backend must be aware of the header.
add proxy-address-forwarding="true" on the http listener and two filter-ref
<http-listener name="default" socket-binding="http" proxy-address-forwarding="true"/>
<filter-ref name="server-header"/>
<filter-ref name="x-powered-by-header"/>
Hope this help,
For Apache Tomcat server, which is running behind AWS Application load balancer, need to enable the RemoteIPValue so that based on the x-forwarded-proto header, Tomcat will overwrite scheme(https) & port(443) accordingly.
In server.xml
<Valve className="org.apache.catalina.valves.RemoteIpValve" protocolHeader="X-Forwarded-Proto" internalProxies="10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|169\.254\.\d+\.\d+|127\.\d+\.\d+\.\d+|172\.(1[6-9]|2[0-9]|3[0-1])\.\d+\.\d+" />

Spring Integration Multipart form Uploads route with JSON response not working

I have a spring integration route in xml which will make a web service call (multipart/formdata) and need to return the response in JSON format. The problem is I don't find any good sample SI route that do a multipart request with response. Any help is really appreciated.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
<bean id="byteArrayHttpMessageConverter"
class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
</bean>
<bean id="formHttpMessageConverter"
class="org.springframework.http.converter.FormHttpMessageConverter">
</bean>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
<bean id="headerMapper"
class="org.springframework.integration.http.support.DefaultHttpHeaderMapper">
<property name="inboundHeaderNames" value="*" />
<property name="outboundHeaderNames" value="*" />
<property name="userDefinedHeaderPrefix" value="" />
</bean>
<int:channel id="http.request.submit.withfiles" />
<int:channel id="http.response.submit.withfiles" />
<int:channel id="http.router.route1.process.submit.withfiles" />
<int:channel id="http.router.route2.process.submit.withfiles" />
<int-http:inbound-gateway id="http.gateway.inbound.submit.withfiles"
supported-methods="POST" header-mapper="headerMapper"
request-channel="http.request.submit.withfiles"
reply-channel="http.response.submit.withfiles" path="/v1.0/file">
<int-http:request-mapping consumes="multipart/form-data"
produces="application/json" />
<int-http:header name="routingCode" expression="headers['routingCode']" />
</int-http:inbound-gateway>
<int:header-value-router input-channel="http.request.submit.withfiles"
header-name="routingCode" default-output-channel="http.router.route2.process.submit.withfiles">
<int:mapping value="AB"
channel="http.router.route1.process.submit.withfiles" />
<int:mapping value="AC"
channel="http.router.route2.process.submit.withfiles" />
<int:mapping value="AD"
channel="http.router.route2.process.submit.withfiles" />
<int:mapping value="AE"
channel="http.router.route2.process.submit.withfiles" />
<int:mapping value="AF"
channel="http.router.route2.process.submit.withfiles" />
</int:header-value-router>
<int-http:outbound-gateway
id="http.gateway.outbound.route1.submit.withfiles" header-mapper="headerMapper"
request-channel="http.router.route1.process.submit.withfiles"
reply-channel="http.response.submit.withfiles"
url="http://localhost:8080/myapplication1/file"
http-method-expression="headers.http_requestMethod"
expected-response-type="java.lang.String" charset="UTF-8"
reply-timeout="50000" />
<int-http:outbound-gateway
id="http.gateway.outbound.route2.submit.withfiles" header-mapper="headerMapper"
request-channel="http.router.route2.process.submit.withfiles"
reply-channel="http.response.submit.withfiles"
url="http://localhost:8081/myapplication2/file"
http-method="POST" charset="UTF-8" expected-response-type="java.lang.String"
reply-timeout="50000">
</int-http:outbound-gateway>
Error Message:
02-Sep-2015 13:36:07.300 SEVERE [http-nio-8080-exec-13] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [springServlet] in context with path [/my-switcher] threw exception [Request processing failed; nested exception is org.springframework.messaging.MessageHandlingException: HTTP request execution failed for URI [http://localhost:8081/myapplication2/file]; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.integration.http.multipart.UploadedMultipartFile["inputStream"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.integration.http.multipart.UploadedMultipartFile["inputStream"])] with root cause
com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.integration.http.multipart.UploadedMultipartFile["inputStream"])
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:26)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:575)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:666)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:129)
at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:2240)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:231)
at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:208)
at org.springframework.http.converter.FormHttpMessageConverter.writePart(FormHttpMessageConverter.java:331)
at org.springframework.http.converter.FormHttpMessageConverter.writeParts(FormHttpMessageConverter.java:311)
at org.springframework.http.converter.FormHttpMessageConverter.writeMultipart(FormHttpMessageConverter.java:301)
at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:240)
at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:87)
at org.springframework.web.client.RestTemplate$HttpEntityRequestCallback.doWithRequest(RestTemplate.java:774)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:567)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:545)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:466)
at org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:422)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:99)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:286)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:245)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:95)
at org.springframework.integration.router.AbstractMessageRouter.handleMessageInternal(AbstractMessageRouter.java:188)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:286)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:150)
at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessagingTemplate.sendAndReceive(AbstractMessagingTemplate.java:42)
at org.springframework.integration.gateway.MessagingGatewaySupport.doSendAndReceive(MessagingGatewaySupport.java:331)
at org.springframework.integration.gateway.MessagingGatewaySupport.sendAndReceiveMessage(MessagingGatewaySupport.java:302)
at org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport.actualDoHandleRequest(HttpRequestHandlingEndpointSupport.java:492)
at org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport.doHandleRequest(HttpRequestHandlingEndpointSupport.java:389)
at org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway.handleRequest(HttpRequestHandlingMessagingGateway.java:103)
at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:51)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1517)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1474)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
More Details:
The actual webservice (http://localhost:8081/myapplication2/file) do a multipart form upload and returns a json message back to the calling application. So what I am expecting from this SI route is the http-outbound gateway will call the above webservice and do a postupload and returns a json response .
Also I am passing the request via postman like this.
Proxying a multipart/form-data request like this is not currently supported.
The problem is that Spring MVC has converted the raw form data to a MultipartHttpInputMessage; which is further converted by the inbound gateway to a MultiValueMap, where the file part(s) is(are) UploadedMultipartFile instances.
The outbound gateway does not know how to process this object; there's not a converter for it.
You could try adding a transformer, to convert the UploadedMultiPartFile element(s) in the payload to Resource(s), or write a custom MessageConverter and inject it into the outbound gateway.
We are seeing more and more of these HTTP "proxy" scenarios, so please open a 'new feature' JIRA issue.
Even better, consider contributing a solution!
EDIT:
Here is another work around - and it is more efficient. Effectively we want to disable multi-part decoding in the inbound gateway, and pass the multi-part request unchanged to the outbound.
Here's how to do it...
Remove the multipart resolver bean
Add a custom "pass-thru" multi-part converter to the inbound gateway...
public class PassThroughMultiPartConverter implements HttpMessageConverter<byte[]> {
#Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
if (!(byte[].class.isAssignableFrom(clazz))) {
return false;
}
if (mediaType != null) {
return MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType)
|| MediaType.MULTIPART_FORM_DATA.includes(mediaType);
}
else {
return false;
}
}
#Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return false;
}
#Override
public List<MediaType> getSupportedMediaTypes() {
return Collections.singletonList(MediaType.MULTIPART_FORM_DATA);
}
#Override
public byte[] read(Class<? extends byte[]> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileCopyUtils.copy(inputMessage.getBody(), baos);
return baos.toByteArray();
}
#Override
public void write(byte[] t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
throw new UnsupportedOperationException();
}
}
.
<int-http:inbound-gateway
...
request-payload-type="byte[]"
message-converters="converter"
merge-with-default-converters="false" />
<bean id="converter" class="foo.PassThroughMultiPartConverter" />
Thanks Gary.
As of now I resolved this issue by replacing the 'http outbound gate way' with 'service activator' and invoke the webservice via REST Template inside the class which is referred in the service activator.
<int:service-activator id="serviceactivator"
input-channel="http.router.process.submit.withfiles"
output-channel="http.response.submit.withfiles"
ref="multipartReceiver" method="receive"
requires-reply="true" >
</int:service-activator>

Spring-WS Wss4jSecurityInterceptor doesn't decrypt received message

My case is as follows:
I wrote SOAP service using CXF
I wrote two SOAP clients - one using CXF and another using Spring-WS
I set up WSSecurity : "Timestamp Signature Encrypt" action on both sides (client / server)
CXF client works like charm, but Spring-WS cant decrypt response.
Server side is OK while interacting with CXF-client and Spring-client (it is properely decrypting, verifing signature, processing requests, sign, encrypt and finnaly send response).
Code:
CXF client:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<bean id="inbound-logging" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<bean id="outbound-logging" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
<jaxws:client id="helloClient" serviceClass="com.example.HelloWorld"
address="http://localhost:8282/HelloWorld">
<jaxws:inInterceptors>
<ref bean="inbound-logging" />
<ref bean="inbound-security" />
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<ref bean="outbound-logging" />
<ref bean="outbound-security" />
</jaxws:outInterceptors>
</jaxws:client>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor" id="outbound-security">
<constructor-arg>
<map>
<entry key="action" value="Timestamp Signature Encrypt"/>
<entry key="user" value="client"/>
<entry key="signaturePropFile" value="config/client-crypto.properties"/>
<entry key="encryptionPropFile" value="config/client-crypto.properties"/>
<entry key="signatureKeyIdentifier" value="DirectReference"/>
<entry key="encryptionUser" value="server"/>
<entry key="passwordCallbackClass" value="org.mydomain.ClientPasswordCallback"/>
<entry key="signatureParts" value="{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp;{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body"/>
<entry key="encryptionParts" value="{Element}{http://www.w3.org/2000/09/xmldsig#}Signature;{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"/>
<entry key="encryptionSymAlgorithm" value="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
</map>
</constructor-arg>
</bean>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor" id="inbound-security">
<constructor-arg>
<map>
<entry key="action" value="Timestamp Signature Encrypt"/>
<entry key="signaturePropFile" value="config/client-crypto.properties"/>
<entry key="decryptionPropFile" value="config/client-crypto.properties"/>
<entry key="passwordCallbackClass" value="org.mydomain.CustomPasswordCallback"/>
</map>
</constructor-arg>
</bean>
Spring-WS client - wss-interceptor :
#Bean
public Wss4jSecurityInterceptor wssInterceptor(
#Qualifier("cryptoFactoryBean") CryptoFactoryBean cryptoFactoryBean,
#Qualifier("signValidCryptoFactoryBean") CryptoFactoryBean signValidCryptoFactoryBean) throws Exception {
Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
// outgoing securement
interceptor.setSecurementUsername("client");
interceptor.setSecurementPassword("123456");
interceptor.setSecurementSignatureKeyIdentifier("DirectReference");
interceptor.setSecurementActions("Timestamp Signature Encrypt");
interceptor.setSecurementSignatureCrypto(cryptoFactoryBean.getObject());
interceptor.setSecurementSignatureParts(
"{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp;" +
"{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body"
);
interceptor.setSecurementEncryptionCrypto(cryptoFactoryBean.getObject());
interceptor.setSecurementEncryptionUser("server");
interceptor.setSecurementEncryptionSymAlgorithm("http://www.w3.org/2001/04/xmlenc#tripledes-cbc");
interceptor.setSecurementEncryptionParts(
"{Element}{http://www.w3.org/2000/09/xmldsig#}Signature;" +
"{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"
);
// incoming validation:
interceptor.setValidationActions("Timestamp Signature Encrypt");
interceptor.setValidationDecryptionCrypto(cryptoFactoryBean.getObject());
interceptor.setValidationSignatureCrypto(signValidCryptoFactoryBean.getObject());
return interceptor;
}
Logging level is set to debug. Here is the result:
(... - logs related with encrypting data befrore send )
2015-08-09 19:09:45.181 DEBUG WSSecEncrypt:258 - Encryption complete.
2015-08-09 19:09:45.182 DEBUG sent:620 - Sent request [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData]
2015-08-09 19:09:45.223 DEBUG received:678 - Received response [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData] for request [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData]
2015-08-09 19:09:45.224 DEBUG Wss4jSecurityInterceptor:562 - Validating message [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData] with actions [Timestamp Signature Encrypt]
2015-08-09 19:09:45.225 DEBUG WSSecurityEngine:236 - enter processSecurityHeader()
2015-08-09 19:09:45.229 WARN Wss4jSecurityInterceptor:281 - Could not validate request: No WS-Security header found
2015-08-09 19:09:45.229 DEBUG Wss4jSecurityInterceptor:288 - No exception resolver present, creating basic soap fault
Exception in thread "main" org.springframework.oxm.UnmarshallingFailureException: JAXB unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException: unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault"). Expected elements are <{}helloRequest>,<{http://example.com}helloResponse>
at org.springframework.oxm.jaxb.Jaxb2Marshaller.convertJaxbException(Jaxb2Marshaller.java:809)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:730)
at org.springframework.ws.support.MarshallingUtils.unmarshal(MarshallingUtils.java:62)
at org.springframework.ws.client.core.WebServiceTemplate$3.extractData(WebServiceTemplate.java:407)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:596)
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:537)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:384)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:374)
at client.Main.main(Main.java:39)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault"). Expected elements are <{}helloRequest>,<{http://example.com}helloResponse>
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:726)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:247)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:242)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:109)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1131)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:556)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:538)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:60)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:153)
at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:229)
at com.sun.xml.internal.bind.unmarshaller.DOMScanner.scan(DOMScanner.java:112)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:354)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:337)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:127)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:726)
... 12 more
It looks like Spring is trying to process response without decrypting it earlier.
I've found solution to the problem. I didn't set password callback, so wss4j-interceptor couldn't decrypt response. Here is code that fix the issue:
KeyStoreCallbackHandler keyStoreCallbackHandler = new KeyStoreCallbackHandler();
keyStoreCallbackHandler.setPrivateKeyPassword("123456");
interceptor.setValidationCallbackHandler(keyStoreCallbackHandler);
You are receiving a soap fault from the other party. Soap fault messages are not encrypted this is why Wss4jSecurityInterceptor is complaining about the ws-security header.
2015-08-09 19:09:45.229 WARN Wss4jSecurityInterceptor:281 -
**Could not validate request: No WS-Security header found**
It seems also that the processing of the message continue while it should normaly stops. The JAXB marshaller complains that (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault") is not from the context.
2015-08-09 19:09:45.229 DEBUG Wss4jSecurityInterceptor:288 -
No exception resolver present, creating basic soap fault
Exception in thread "main"
org.springframework.oxm.UnmarshallingFailureException:
JAXB unmarshalling exception; nested exception is
javax.xml.bind.UnmarshalException: unexpected element
(uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault").
Expected elements are <{}helloRequest>,<{http://example.com}helloResponse>
I would recommend the following:
Log the soap fault to determine what the other party needs
Whenever you receive a soap fault, you should stop processing the request
KeyStoreCallbackHandler wasn't defined for the interceptor - that was the case. Appropriate code in the initial post.

Spring MVC multipart response

I'm trying to write full multipart flow, from the client side sending multipart request using Spring restTemplate and from the server side auto resolve the different parts to objects (I'm using JAXB for objects marshaling) and send a response back in multipart.
I'm was able to implement almost all the flow but I'm unable to send a multipart response with jaxb objects from spring controller.
Here is the contorller code:
#RequestMapping(value="/putuser",method=RequestMethod.POST)
#ResponseBody
public MultiValueMap<String, Object> getUser(#RequestBody User user) throws IOException, JAXBException {
}
user.setName("new");
MultiValueMap<String, Object> form = new LinkedMultiValueMap<String, Object>();
form.add("user", user);
form.add("file", new FileSystemResource("/tmp/1.1"));
return form;
}
This is the exception that I'm getting in the server side:
java.lang.ClassCastException: org.springframework.core.io.FileSystemResource cannot be cast to java.lang.String
at org.springframework.http.converter.FormHttpMessageConverter.writeForm(FormHttpMessageConverter.java:233)
at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:197)
at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:73)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:148)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:90)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:189)
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:69)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:122)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:746)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:687)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:915)
It looks like Spring is trying to convert each part in the response to String instead of the correct content type (file/xml etc/)
I tried to update my spring.xml file like this:
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.FormHttpMessageConverter">
<property name="partConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter" />
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" />
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
To try making he FormHttpMessageConverter to write the parts correctly but this did not helped
Is there any way making spring to send multipart responses correctly with different types of parts?
You will need to set Content-Type header of your response to appropriate value. The FormHttpMessageConverter relies on Content-Type to identify if it needs to cast the response to String or some other type.
#RequestMapping(value="/putuser",method=RequestMethod.POST)
#ResponseBody
public MultiValueMap<String, Object> getUser(#RequestBody User user, HttpServletResponse httpResponse) throws IOException, JAXBException {
}
user.setName("new");
MultiValueMap<String, Object> form = new LinkedMultiValueMap<String, Object>();
form.add("user", user);
form.add("file", new FileSystemResource("/tmp/1.1"));
httpResponse.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE); // <-- IMPORTANT
return form;
}

Resources