WebClient ExchangeFilterFunction unable to add header java.lang.UnsupportedOperationException ReadOnlyHttpHeaders - spring-boot

I am trying to add a simple ExchangeFilterFunction to a WebClient request. However, I am seeing the following exception:
java.lang.UnsupportedOperationException
at org.springframework.http.ReadOnlyHttpHeaders.add(ReadOnlyHttpHeaders.java:84)
at com.ecs.springframework.reactive.web.filter.AddHeaderExchangeFilter.filter(AddHeaderExchangeFilter.java:24)
at org.springframework.web.reactive.function.client.ExchangeFilterFunction.lambda$apply$2(ExchangeFilterFunction.java:66)
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.exchange(DefaultWebClient.java:319)
at com.ecs.springframework.reactive.web.filter.AddHeaderExchangeFilterTest.whenExchangeFilterFunctionInjectedIntoWebClient_thenWebClientShouldPropagate(AddHeaderExchangeFilterTest.java:57)
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:498)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:532)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at
ExchangeFilterFunction
public class AddHeaderExchangeFilter implements ExchangeFilterFunction {
#Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
ClientRequest newRequest = ClientRequest.create(request.method(), request.url()).build();
newRequest.headers().add("EFF-TEST-HEADER", "EFF-TEST-VALUE");
return next.exchange(newRequest);
}
}
WebClient webTestClient = WebClient.builder().filter(new AddHeaderExchangeFilter()).build();
webTestClient.get().uri("http://httpbin.org/get").exchange();
I am relatively new to WebFlux so any pointers would be appreciated.

You need to add the headers when you creating newRequest:
public class AddHeaderExchangeFilter implements ExchangeFilterFunction {
#Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
ClientRequest newRequest = ClientRequest.from(request)
.header("EFF-TEST-HEADER", "EFF-TEST-VALUE")
.build();
return next.exchange(newRequest);
}
}
The reason for your current behavior is that ClientRequest.create initiating new DefaultClientRequestBuilder.
You can see in the Spring's source code that once you build() your request the builder building the request with read only headers, and therefore you can't add the headers after you built the ClientRequest.

Related

How to create custom error response in Spring Integration using java DSL

I have following simple proxy integration flow. The main task of which is to take request from the proxy send it to the actual endpoint, get the respond and send it back to the client.
#SpringBootApplication
#EnableIntegration
public class IntegrationApp {
#Value("${narko.pin}")
private String pinUrl;
public static void main(String[] args) {
SpringApplication.run(MinzdravApplication.class, args);
}
#Bean
public DirectChannel requestPinChannel() {
return new DirectChannel();
}
#Bean
public DirectChannel replyPinChannel() {
return new DirectChannel();
}
#Bean
public IntegrationFlow httpProxyFlowPin() throws Exception {
return IntegrationFlows
.from(Http.inboundGateway("/narko/api/patient/by-pinpp")
.requestChannel(requestPinChannel()).replyChannel(replyPinChannel())
.mappedRequestHeaders("activityid")
.errorChannel("httpProxyErrorFlow.input")
)
.wireTap(sf->sf.handle(new InwardMessageHandler()))
.enrichHeaders(h -> h.header("Content-Type", "application/json"))
.handle(Http.outboundGateway(pinUrl).charset("utf-8")
.expectedResponseType(String.class))
.channel(replyPinChannel())
.get();
}
#Bean
public IntegrationFlow httpProxyErrorFlow() {
return f -> f
.transform(Throwable::getCause)
.<HttpClientErrorException>handle((p, h) ->
new RuntimeException("custom exception"));
}
}
When the api at the outbound gateway is down. I have following error:
{
"timestamp": "2022-08-10T12:51:58.561+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/narko/api/patient/by-pinpp"
}
And I have following exceptions on logs:
java.lang.ClassCastException: class org.springframework.web.client.ResourceAccessException cannot be cast to class org.springframework.web.client.HttpClientErrorException (org.springframework.web.client.ResourceAccessException and org.springframework.web.client.HttpClientErrorException are in unnamed module of loader 'app')
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.integration.handler.LambdaMessageProcessor.processMessage(LambdaMessageProcessor.java:104) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.handler.ServiceActivatingHandler.handleRequestMessage(ServiceActivatingHandler.java:105) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:136) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:56) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:115) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:133) ~[spring-integration-core-5.5.10.jar:5.5.10]
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:106) ~[spring-integration-core-5.5.10.jar:5.5.10]
How can I create custom exception response?
Any navigation or hint is appreciated.
First of all you don't need that .replyChannel(replyPinChannel() and .channel(replyPinChannel()). An inbound gateway sends a message with a replyChannel header, the last replying endpoint in the flow, not founding its outputChannel, will consult with that replyChannel.
Secondly, your solution about an error handler is OK, but you see yourself in the stacktrace that you just don't cast to the proper type: the ResourceAccessException is not an instance of HttpClientErrorException. Consider to expect a RestClientException instead which is a super for both ResourceAccessException and HttpClientErrorException.

Spring WebFlux authenticated WebSocket connection

I have running Spring Boot#2.2.x server with exposed WebSocket endpoint. Here is my WebSocketConfiguration:
#Slf4j
#Configuration
public class WebSocketConfiguration {
private static final String WS_PATH = "/ws/notifications";
#Bean
public HandlerMapping webSocketHandlerMapping() {
Map<String, WebSocketHandler> handlersMap = new HashMap<>();
handlersMap.put(WS_PATH, session -> session.send(session.receive()
.map(WebSocketMessage::getPayloadAsText)
.doOnEach(logNext(log::info))
.map(msg -> format("notification for your msg: %s", msg))
.map(session::textMessage)));
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
handlerMapping.setOrder(Ordered.HIGHEST_PRECEDENCE);
handlerMapping.setUrlMap(handlersMap);
return handlerMapping;
}
#Bean
public WebSocketHandlerAdapter handlerAdapter(WebSocketService webSocketService) {
return new WebSocketHandlerAdapter(webSocketService);
}
#Bean
public WebSocketService webSocketService() {
return new HandshakeWebSocketService(new ReactorNettyRequestUpgradeStrategy());
}
}
The question is how I can implement authentication for establishing WS connection either using Basic Authentication or Bearer Authentication or access_token query parameter?
The preferable option is to avoid using Spring Security.
Thanks.
Websocket connection starts out life as an HTTP request that is Upgraded. You can do JWT token authentication before the upgrade happens. In spring boot it works as follows:
Expose a custom WebSocketService bean:
#Bean
public WebSocketService webSocketService(RequestUpgradeStrategy upgradeStrategy) {
return new HandshakeWebSocketService(upgradeStrategy);
}
Implement the RequestUpgradeStrategy interface in your own class:
#Override
public Mono<Void> upgrade(ServerWebExchange exchange, WebSocketHandler handler, #Nullable String subProtocol, Supplier<HandshakeInfo> handshakeInfoFactory) {
ServerHttpResponse response = exchange.getResponse();
HttpServerResponse reactorResponse = getNativeResponse(response);
HandshakeInfo handshakeInfo = handshakeInfoFactory.get();
NettyDataBufferFactory bufferFactory = (NettyDataBufferFactory) response.bufferFactory();
var authResult = validateAuth(handshakeInfo);
if (authResult == unauthorised) return Mono.just(reactorResponse.status(rejectedStatus))
.flatMap(HttpServerResponse::send);
else return reactorResponse.sendWebsocket(subProtocol, //
this.maxFramePayloadLength,//
(in, out) -> {
ReactorNettyWebSocketSession session = new ReactorNettyWebSocketSession(in, out,
handshakeInfo,
bufferFactory,
this.maxFramePayloadLength);
return handler.handle(session);
});
}
Notes:
The above class is based on ReactorNettyRequestUpgradeStrategy.
Returning reactorResponse.sendWebsocket is the existing behaviour that upgrades the connection to a WebSocket connection
reactorResponse.status can be returned to stop the connection being upgraded. For example, you can return a 401 response in the case of an unauthorised connection.
Query params and Authentication headers can be found in handshake info. How to do the authentication itself is outside the scope of the question.

Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'request' is not active for the current thread for feign client

I am calling another microservice once my current microservice is up and ready using feign client in my current microservice built using Jhipster.
So my Feign Interface is
package com.persistent.integration.client;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.persistent.integration.service.dto.DataPipelineDTO;
#AuthorizedFeignClient(name = "Integrationconfiguration")
public interface DataPipelinesResourceFeign {
#RequestMapping(value = "/api/data-pipelines", method = RequestMethod.GET)
List<DataPipelineDTO> getAllDataPipelines(#RequestParam(value = "pageable") Pageable pageable );
}
}
And I have implemented ApplicationRunner where I have called feign client method.
#Component
public class ApplicationInitializer implements ApplicationRunner {
#Autowired
private DataPipelinesResourceFeign dataPipelinesResourceFeign;
#Autowired
private ActiveMQListener activeMqListener;
#Override
public void run(ApplicationArguments args) throws Exception {
// TODO Auto-generated method stub
Pageable pageable = PageRequest.of(0, 20);
try {
List <DataPipelineDTO> allStartedDataPipeLines = dataPipelinesResourceFeign.getAllDataPipelines(pageable); //.stream().filter(p->p.getState().equals(State.STARTED)).collect(Collectors.toList());
allStartedDataPipeLines.forEach(datapipe ->
{
try {
activeMqListener.consume(datapipe);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
But after running this, it gives below exception at dataPipelinesResourceFeign.getAllDataPipelines :
com.netflix.hystrix.exception.HystrixRuntimeException: DataPipelinesResourceFeign#getAllDataPipelines(Pageable) failed and no fallback available.
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:819)
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:804)
at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at com.netflix.hystrix.AbstractCommand$DeprecatedOnFallbackHookApplication$1.onError(AbstractCommand.java:1472)
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'scopedTarget.oauth2ClientContext':
Scope 'request' is not active for the current thread; consider
defining a scoped proxy for this bean if you intend to refer to it
from a singleton; nested exception is java.lang.IllegalStateException:
No thread-bound request found: Are you referring to request attributes
outside of an actual web request, or processing a request outside of
the originally receiving thread? If you are actually operating within
a web request and still receive this message, your code is probably
running outside of DispatcherServlet/DispatcherPortlet: In this case,
use RequestContextListener or RequestContextFilter to expose the
current request. at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(Abstrac>tBeanFactory.java:362)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractB>eanFactory.java:199)
at
org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTarge>tSource.java:35)
at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.>java:193)
at com.sun.proxy.$Proxy147.getAccessToken(Unknown Source) at
com.persistent.integration.security.oauth2.AuthorizationHeaderUtil.getAuthoriza>tionHeaderFromOAuth2Context(AuthorizationHeaderUtil.java:28)
at
com.persistent.integration.client.TokenRelayRequestInterceptor.apply(TokenRelay>RequestInterceptor.java:23)
at
feign.SynchronousMethodHandler.targetRequest(SynchronousMethodHandler.java:158)
at
feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:88)
at
feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
at
feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298)
at
rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
... 68 more Caused by: java.lang.IllegalStateException: No
thread-bound request found: Are you referring to request attributes
outside of an actual web request, or processing a request outside of
the originally receiving thread? If you are actually operating within
a web request and still receive this message, your code is probably
running outside of DispatcherServlet/DispatcherPortlet: In this case,
use RequestContextListener or RequestContextFilter to expose the
current request. at
org.springframework.web.context.request.RequestContextHolder.currentRequestAttr>ibutes(RequestContextHolder.java:131)
at
org.springframework.web.context.request.AbstractRequestAttributesScope.get(Abst>ractRequestAttributesScope.java:42)
at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(Abstrac>tBeanFactory.java:350)
many suggestions on internet were to add listerner RequestContextListener. But problem persisted even if I added listener in webConfigurer.java in onStartup method.
{
servletContext.addListener(RequestContextListener.class);
}
But of no use.
Any leads would be appreciated.
I found a workaround for this. I don't know why TokenRelayRequestIntercepton isn't working but you can use your own RequestInterceptor based on Spring's SecurityContext.
First, define a RequestInterceptor :
public class MyRequestInterceptor implements RequestInterceptor {
public static final String AUTHORIZATION = "Authorization";
public static final String BEARER = "Bearer";
public MyRequestInterceptor() {
super();
}
#Override
public void apply(RequestTemplate template) {
// demander un token à keycloak et le joindre à la request
Optional<String> header = getAuthorizationHeader();
if (header.isPresent()) {
template.header(AUTHORIZATION, header.get());
}
}
public static Optional<String> getAuthorizationHeader() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getDetails() != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails oAuth2AuthenticationDetails =
(OAuth2AuthenticationDetails) authentication.getDetails();
return Optional.of(String.format("%s %s", oAuth2AuthenticationDetails.getTokenType(),
oAuth2AuthenticationDetails.getTokenValue()));
} else {
return Optional.empty();
}
}
}
and then, declare a config class for your feign client using your RequestInterceptor, it should contains something like this :
#Bean(name = "myRequestInterceptor")
public RequestInterceptor getMyRequestInterceptor() throws IOException {
return new MyRequestInterceptor();
}
Your Feign client shoud look like this:
#FeignClient(name = "SERVICE_NAME", configuration = MyFeignConfiguration.class)
public interface MyRestClient {
I had the same issue with Feign Client running on startup using ApplicationRunner and I came up with following solution.
I defined my FeignClientsConfiguration with OAuth2FeignRequestInterceptor, which accepts predefined bean DefaultOAuth2ClientContext and OAuth2 configuration OAuth2ProtectedResourceDetails:
#Configuration
public class MyConfig extends FeignClientsConfiguration {
#Bean
public RequestInterceptor oauth2FeignRequestInterceptor( DefaultOAuth2ClientContext oAuth2ClientContext, MyOauth2Properties properties) {
return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resourceDetails(properties));
}
#Bean
public DefaultOAuth2ClientContext oAuth2ClientContext() {
return new DefaultOAuth2ClientContext();
}
private OAuth2ProtectedResourceDetails resourceDetails(MyOauth2Properties oauth2Properties) {
ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
resourceDetails.setAccessTokenUri(oauth2Properties.getAccessTokenUri());
resourceDetails.setUsername(oauth2Properties.getUsername());
resourceDetails.setPassword(oauth2Properties.getPassword());
resourceDetails.setClientId(oauth2Properties.getClientId());
return resourceDetails;
}
}
Your feign client will look something like this:
#FeignClient(url = "http://localhost:8080/api/v1")
public interface FeignClient {
}
After all this, calling FeignClient from ApplicationRunner.run() works fine.
Spring Boot 2.2.6

How to send HTTP OPTIONS request with body using Spring rest template?

I am trying to call a RESTfull web service resource, this resource is provided by a third party, the resource is exposed with OPTIONS http verb.
To integrate with the service, I should send a request with a specific body, which identities by a provider, but when I did that I got a bad request. After that I trace my code then I recognized that the body of the request is ignored by rest template based on the below code:
if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
"PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) {
connection.setDoOutput(true);
}
else {
connection.setDoOutput(false);
}
my question, is there a standard way to override this behavior or I should use another tool?
The code you've pasted is from
SimpleClientHttpRequestFactory.prepareConnection(HttpURLConnection connection, String httpMethod)
I know because I've debugged that code few hours ago.
I had to do a HTTP GET with body using restTemplate. So I've extend SimpleClientHttpRequestFactory, override prepareConnection and create a new RestTemplate using the new factory.
public class SimpleClientHttpRequestWithGetBodyFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
if ("GET".equals(httpMethod)) {
connection.setDoOutput(true);
}
}
}
Create a new RestTemplate based on this factory
new RestTemplate(new SimpleClientHttpRequestWithGetBodyFactory());
A test to prove the solution is working using spring boot (#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT))
public class TestRestTemplateTests extends AbstractIntegrationTests {
#Test
public void testMethod() {
RestTemplate restTemplate = new RestTemplate(new SimpleClientHttpRequestWithBodyForGetFactory());
HttpEntity<String> requestEntity = new HttpEntity<>("expected body");
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:18181/test", HttpMethod.GET, requestEntity, String.class);
assertThat(responseEntity.getBody()).isEqualTo(requestEntity.getBody());
}
#Controller("/test")
static class TestController {
#RequestMapping
public #ResponseBody String testMethod(HttpServletRequest request) throws IOException {
return request.getReader().readLine();
}
}
}

Issue injecting #Context in resource class

I'm having difficulties injecting HttpHeaders into my rest service class.
#Path("/account")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes({ MediaType.APPLICATION_JSON })
#Transactional
#Service
public class UserServiceImpl implements UserService {
#Context
private HttpHeaders headers;
#Override
#POST #Path("/{username}/")
public User get(#PathParam(value = "username") String username)
throws UnknownUserException {
String requestId = headers.getHeaderString("requestId");
return new User();
}
}
When I try running the application I get the following exception during the spring initialisation:
Caused by: java.lang.IllegalArgumentException: Can not set javax.ws.rs.core.HttpHeaders field com.acme.service.UserServiceImpl.headers to com.sun.proxy.$Proxy50
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
at java.lang.reflect.Field.set(Field.java:758)
at org.apache.cxf.jaxrs.utils.InjectionUtils$1.run(InjectionUtils.java:192)
at java.security.AccessController.doPrivileged(Native Method)
at org.apache.cxf.jaxrs.utils.InjectionUtils.injectFieldValue(InjectionUtils.java:188)
at org.apache.cxf.jaxrs.utils.InjectionUtils.injectContextProxiesAndApplication(InjectionUtils.java:1058)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.injectContexts(JAXRSServerFactoryBean.java:405)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.updateClassResourceProviders(JAXRSServerFactoryBean.java:429)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:162)
A bit of experimenting shows that I get the same error when trying to inject anything. I have an exception mapper that has the http headers injected in do problem what so ever, so I created a custom provider to obtain the headers but get the same problem injecting that in.
I'm pretty sure I must be missing something fundamental here.
I know that I could add what I need out of the headers as params to the method but I can't change the interface.
Adding the context to the operation
#Override
#POST #Path("/{username}/")
public User get(#Context HttpHeaders httpHeaders, #PathParam(value = "username") String username)
Gives me the following error.
java.lang.IllegalArgumentException: object is not an instance of declaring class
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:483)
at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:181)
at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:97)
I've discovered that if I drop the implements interface then the application actually starts, however nothing is actually injected in.
I've found a workaround (or possibly the expected solution). It appears that spring is using a proxy based on the interface. I created an intermediate interface and stuck on a setHttpHeaders operation on the interface and annotated the implementation with #Context. All seems fine with that.
public interface MyService {
void doStuff();
}
public interface MyServiceInt extends MyService {
void setHttpHeaders(HttpHeaders headers);
}
#Path("/")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes({ MediaType.APPLICATION_JSON })
#Transactional
#Service
public class MyServiceImpl implements MyServiceInt {
private HttpHeaders httpHeaders;
#Context
void setHttpHeaders(HttpHeaders headers) {
this.httpHeaders = httpHeaders;
}
#Override
#POST #Path("/doStuff")
public void doStuff() {
}
}
Would still like to hear of a better solution if anyone knows.

Resources