Resource not invoked after Jersery Request Filter - spring

I have a two SpringServlets defined, one of which directs to a custom filter
#WebServlet(urlPatterns = { "/" }, initParams = {
#WebInitParam(name = "com.sun.jersey.config.property.packages", value = "com.x.y.resource"),
#WebInitParam(name = "com.sun.jersey.api.json.POJOMappingFeature", value = "true") })
public class JerseyServlet extends SpringServlet
{
}
#WebServlet(name = "secure", urlPatterns = "/secure/*", initParams = {
#WebInitParam(name = "com.sun.jersey.config.property.packages", value = "com.x.y.resource"),
#WebInitParam(name = "com.sun.jersey.spi.container.ContainerRequestFilters", value = "com.x.y.resource.OAuthFilter"),
#WebInitParam(name = "com.sun.jersey.api.json.POJOMappingFeature", value = "true") })
public class JerseySecureServlet extends SpringServlet
{
}
The idea being that any URLs that contain "/secure/" are directed to the OAuthFilter to validate the request by the OAuth headers. All other URLs are just handled normally.
For any request sent to a URL not containing "/secure/", the appropriate JAX-RS resource class is invoked correctly. However, for URLs containing "/secure/" the OAuthFilter is invoked correctly, but the JAX-RS annotated Resource class is never invoked afterwards.
Here is an example secure resource
#Component
#Scope("request")
#Path("/secure/example")
public class SecureResource
{
#GET
...
}
and here is the OAuthFilter
#Provider
#Component
public class OAuthFilter implements ContainerRequestFilter
{
public static final String AUTHORIZED_USER = "authorized_user";
#Autowired
AccessTokenService accessTokenService;
#Autowired
UserService userService;
#Context
HttpServletRequest httpRequest;
#Override
public ContainerRequest filter(ContainerRequest containerRequest)
{
OAuthServerRequest request = new OAuthServerRequest(containerRequest);
OAuthParameters params = new OAuthParameters();
params.readRequest(request);
String accessToken = params.getToken();
if (accessToken == null)
{
throw new WebApplicationException(Status.UNAUTHORIZED);
}
String userId = accessTokenService.getUserIdForToken(accessToken);
if (userId == null)
{
throw new WebApplicationException(Status.UNAUTHORIZED);
}
User user = userService.get(userId);
if (user == null)
{
throw new WebApplicationException(Status.NOT_FOUND);
}
httpRequest.setAttribute(AUTHORIZED_USER, user);
return containerRequest;
}
}
It looks like once the JerseySecureServlet with the "/secure/*" mapping is selected and the OAuthFilter is invoked the baseURI is "http:/ip:port/context/secure" and the path is simply "/example", and no Resource corresponds to this path, so nothing is invoked. What should I be doing instead to only apply this filter to URLs that contain "/secure/"?

I have solved the problem, but I am not 100% sure if my solution is the correct way to do it. I have changed the annotation on the filtered serlvet to be #WebFilter, giving me
#WebServlet(urlPatterns = { "/*" }, initParams = {
#WebInitParam(name = "com.sun.jersey.config.property.packages", value = "com.x.y.resource"),
#WebInitParam(name = "com.sun.jersey.api.json.POJOMappingFeature", value = "true") })
public class JerseyServlet extends SpringServlet
{
}
#WebFilter(urlPatterns = "/secure/*", initParams = {
#WebInitParam(name = "com.sun.jersey.config.property.packages", value = "com.x.y.resource"),
#WebInitParam(name = "com.sun.jersey.spi.container.ContainerRequestFilters", value = "com.x.y.resource.OAuthFilter"),
#WebInitParam(name = "com.sun.jersey.api.json.POJOMappingFeature", value = "true") })
public class JerseySecureFilter extends SpringServlet
{
}
and this works. A better solution will be accepted over this one.

Related

WebFlux API-Layer Test returns 404

I'm trying to get started with Spring WebFlux with Spring Boot 3.0
I'm Building a Person API with an open api generator.
The Application runs and gives the expected results when it is tested manually.
But I'm not able to get the API layer unit tested.
This is my Test Class
#WebFluxTest(controllers = {PersonApiController.class})
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {PersonMapperImpl.class, H2PersonRepository.class, PersonRepository.class})
#DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class PersonRouterTest {
#MockBean
private PersonService personService;
#Autowired
private WebTestClient client;
#ParameterizedTest
#CsvSource({"1234, Max Mustermann", "5678, Erika Musterfrau"})
void retrieve_a_name(String id, String name) {
when(personService.getPersonDataByID(1234)).thenReturn(Mono.just(new PersonData(1234, "Max Mustermann")));
when(personService.getPersonDataByID(5678)).thenReturn(Mono.just(new PersonData(5678, "Erika Musterfrau")));
client.get()
.uri(uriBuilder -> uriBuilder
.path("/persons/{id}")
.build(id))
.accept(MediaType.ALL)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBody()
.jsonPath("$.name").isEqualTo(name);
}
This is my Controller Class
#Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2022-12-
09T09:14:36.692713900+01:00[Europe/Vienna]")
#Controller
#RequestMapping("${openapi.openAPIDefinition.base-path:}")
public class PersonApiController implements PersonApi {
private final PersonApiDelegate delegate;
public PersonApiController(#Autowired(required = false) PersonApiDelegate delegate) {
this.delegate = Optional.ofNullable(delegate).orElse(new PersonApiDelegate() {});
}
#Override
public PersonApiDelegate getDelegate() {
return delegate;
}
}
The API interface:
#Tag(
name = "Person",
description = "the Person API"
)
public interface PersonApi {
default PersonApiDelegate getDelegate() {
return new PersonApiDelegate() {
};
}
#Operation(
operationId = "findPersonById",
summary = "Find Person by ID",
tags = {"Person"},
responses = {#ApiResponse(
responseCode = "200",
description = "successful operation",
content = {#Content(
mediaType = "application/json",
schema = #Schema(
implementation = PersonData.class
)
)}
)}
)
#RequestMapping(
method = {RequestMethod.GET},
value = {"/persons/{id}"},
produces = {"application/json"}
)
default Mono<ResponseEntity<PersonData>> findPersonById(#Parameter(name = "id",description = "Person ID",required = true) #PathVariable("id") Integer id, #Parameter(hidden = true) final ServerWebExchange exchange) {
return this.getDelegate().findPersonById(id, exchange);
}
#Operation(
operationId = "savePerson",
summary = "Creates a new Person",
tags = {"Person"},
responses = {#ApiResponse(
responseCode = "200",
description = "successful operatoin",
content = {#Content(
mediaType = "application/json",
schema = #Schema(
implementation = PersonData.class
)
)}
)}
)
#RequestMapping(
method = {RequestMethod.POST},
value = {"/persons"},
produces = {"application/json"},
consumes = {"application/json"}
)
default Mono<ResponseEntity<PersonData>> savePerson(#Parameter(name = "PersonData",description = "") #RequestBody(required = false) Mono<PersonData> personData, #Parameter(hidden = true) final ServerWebExchange exchange) {
return this.getDelegate().savePerson(personData, exchange);
}
}
and finally my delegate impl:
#Service
public class PersonDelegateImpl implements PersonApiDelegate {
public static final Mono<ResponseEntity<?>> RESPONSE_ENTITY_MONO = Mono.just(ResponseEntity.notFound().build());
private final PersonService service;
private final PersonMapper mapper;
public PersonDelegateImpl(PersonService service, PersonMapper mapper) {
this.service = service;
this.mapper = mapper;
}
public static <T> Mono<ResponseEntity<T>> toResponseEntity(Mono<T> mono) {
return mono.flatMap(t -> Mono.just(ResponseEntity.ok(t)))
.onErrorResume(t -> Mono.just(ResponseEntity.internalServerError().build()));
}
#Override
public Mono<ResponseEntity<PersonData>> findPersonById(Integer id, ServerWebExchange exchange) {
Mono<com.ebcont.talenttoolbackend.person.PersonData> personDataByID = service.getPersonDataByID(id);
return toResponseEntity(personDataByID.map(mapper::map));
}
#Override
public Mono<ResponseEntity<PersonData>> savePerson(Mono<PersonData> personData, ServerWebExchange exchange) {
return PersonApiDelegate.super.savePerson(personData, exchange);
If I run the test class I always get:
< 404 NOT_FOUND Not Found
< Content-Type: [application/json]
< Content-Length: [139]
{"timestamp":"2022-12-09T08:45:41.278+00:00","path":"/persons/1234","status":404,"error":"Not Found","message":null,"requestId":"4805b8b8"}
I have tried to change the Context Configuration but I did not get it to work.
I found the Problem, changing the Test Config to :
#WebFluxTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {PersonMapperImpl.class, H2PersonRepository.class, PersonRepository.class, PersonApiController.class, PersonDelegateImpl.class})
#DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
Solved my Problem.
The Controller bean was not recognized. I had to add PersonApiCrontroller and PersonDelegateImpl to the Context Config. i then removed the PersonApiController from the #WebFluxTest annotation.

FeignException com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.http.ResponseEntity`

Any Help please !!
I receive this error when I'm calling my endpoint which call Feign in the background :
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
`org.springframework.http.ResponseEntity` (no Creators, like default constructor, exist): cannot deserialize
from Object value (no delegate- or property-based Creator)
at [Source: (BufferedReader); line: 1, column: 2]
This is my endpoint inside Controller :
#RestController
#RequestMapping(Routes.URI_PREFIX)
public class CartoController {
#Autowired
private ReadCartographyApiDelegate readCartographyApiDelegate;
#GetMapping(value = "/cartographies/{uid}", produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseWrapper<ReadCartographyResponse> readCarto(HttpServletRequest request,
#PathVariable(name = "uid") String uid) {
ResponseEntity<ReadCartographyResponse> result ;
try {
result = readCartographyApiDelegate.readCartography(uid);
}catch (Exception e){
throw new TechnicalException("Error during read Carto");
}
return responseWrapperWithIdBuilder.of(result.getBody());
}
}
Interface ReadCartographyApiDelegate generated automatically by openApi from yaml file :
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "...")
public interface ReadCartographyApiDelegate {
default Optional<NativeWebRequest> getRequest() {
return Optional.empty();
}
default ResponseEntity<ReadCartographyResponse> readCartography(String uid) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "null";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
This my ReadCartoApiDelegateImpl which implements ReadCartographyApiDelegate interface :
#Service
public class ReadCartographyApiDelegateImpl implements ReadCartographyApiDelegate {
private EcomGtmClient ecomGtmClient;
public ReadCartographyApiDelegateImpl(EcomGtmClient ecomGtmClient) {
this.ecomGtmClient = ecomGtmClient;
}
#Override
public ResponseEntity<ReadCartographyResponse> readCartography(String uid) {
ResponseEntity<ReadCartographyResponse> response = ecomGtmClient.readCartography(uid);
return response;
}
}
This is the feign client :
#FeignClient(name = "ecomGtmSvc", url = "http://localhost/")
public interface EcomGtmClient {
#GetMapping(value = "/read-carto/{uid}")
ResponseEntity<ReadCartographyResponse> readCartography(#PathVariable("uid") String uid);
}
The problem is that ResponseEntity (spring class) class doesn't contain default constructor which is needed during creating of instance. is there Any config to resolve this issue ?
If you want access to the body or headers on feign responses, you should use the feign.Response class. ResponseEntity does not work with feign because it is not meant to. I think it is best if you just return Response from your feign client method. You should then be able to pass the body to the ResponseEntity instance in the Controller.
What is your reason to even use the response-wrapper, i can't really figure that out from your code?
Sadly I couldn't find any documentation on the Response class, but here's the link to the source on GitHub.
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Response.java
My Suggestion would be
#FeignClient(name = "ecomGtmSvc", url = "http://localhost/")
public interface EcomGtmClient {
#GetMapping(value = "/read-carto/{uid}")
ReadCartographyResponse readCartography(#PathVariable("uid") String uid);
}
#RestController
#RequestMapping(Routes.URI_PREFIX)
public class CartoController {
#Autowired
private ReadCartographyApiDelegate readCartographyApiDelegate;
#GetMapping(value = "/cartographies/{uid}", produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseWrapper<ReadCartographyResponse> readCarto(HttpServletRequest request,
#PathVariable(name = "uid") String uid) {
ReadCartographyResponse result ;
try {
result = readCartographyApiDelegate.readCartography(uid);
}catch (Exception e){
throw new TechnicalException("Error during read Carto");
}
// I don't know where you get the builder from, so I assume it does something import and is needed
return responseWrapperWithIdBuilder.of(result);
}
}
Of course you'd also have to change all intermediate classes.
The Response Output was the correct Object that I have to put, cause every time I need to check the status from my feign client endpoint to do différent logic
#FeignClient(name = "ecomGtmSvc", url = "http://localhost/")
public interface EcomGtmClient {
#GetMapping(value = "/read-carto/{uid}")
ReadCartographyResponse readCartography(#PathVariable("uid") String uid);
}

Receiving request parameters as null for SOAP API in STS

I'm calling an endpoint from SOAP-UI for a POST request.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:fet="http://www.example.com/fetchMKDataRequest.xsd">
<soapenv:Header/>
<soapenv:Body>
<fet:fetchMKDataRequest>
<fet:MKDataList>
<fet:CompanyCode>123</fet:CompanyCode>
<fet:PersonnelAreaCode>XYZ</fet:PersonnelAreaCode>
</fet:MKDataList>
</fet:fetchMKDataRequest>
</soapenv:Body>
</soapenv:Envelope>
My endpoint is recognised along with the request body. BUT the value of MKDataList appears null as shown below:
My Endpoint class looks like :
#Endpoint
public class FetchMKDataEndpoint {
private static final String NAMESPACE_URI = "http://example.com/fetchMKDataRequest.xsd";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "fetchMKDataRequest")
#ResponsePayload
public FetchMKDataResponse fetchMKData(#RequestPayload FetchMKDataRequest request) {
System.out.println("reached FETCHMKDATA endpoint");
FetchMKDataResponse response = new FetchMKDataResponse();
//commented block of code
return response;
}
}
Request model is as follows. It was generated using jaxb2-maven-plugin. I had to remove the annotations #XmlAccessorType(XmlAccessType.FIELD) & #XmlType for each class. Also I had to assign #XmlElement(name = "MKDataList", required = true), all such #XmlElements at respective GET methods. Without doing these 2 changes, i was getting x counts of IllegalAnnotationExceptions exception in SOAP-UI response.
#XmlRootElement(name = "fetchMKDataRequest", namespace="http://www.example.com/fetchMKDataRequest.xsd")
public class FetchMKDataRequest {
protected List<FetchMKDataRequest.MKDataList> MKDataList;
#XmlElement(name = "MKDataList", required = true)
public List<FetchMKDataRequest.MKDataList> getMKDataList() {
if (MKDataList == null) {
MKDataList = new ArrayList<FetchMKDataRequest.MKDataList>();
}
return this.MKDataList;
}
public void setMKDataList(List<FetchMKDataRequest.MKDataList> mKDataList) {
mKDataList = mKDataList;
}
public static class MKDataList implements Serializable {
private static final long serialVersionUID = 1L;
protected String companyCode;
protected String personnelAreaCode;
#XmlElement(name = "CompanyCode", required = true)
public String getCompanyCode() {
return companyCode;
}
public void setCompanyCode(String value) {
this.companyCode = value;
}
#XmlElement(name = "PersonnelAreaCode", required = true)
public String getPersonnelAreaCode() {
return personnelAreaCode;
}
public void setPersonnelAreaCode(String value) {
this.personnelAreaCode = value;
}
}
}
Can anything be done to get MKDataList object in the request body as NOT null?
Thanks in advance
I made changes in my request model and added package-info.java in the request model package.
Request model class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "mkDataList" })
#XmlRootElement(name = "fetchMKDataRequest", namespace="http://www.example.com/fetchMKDataRequest.xsd")
public class FetchMKDataRequest {
#XmlElement(name = "MKDataList", required = true)
protected List<FetchMKDataRequest.MKDataList> mkDataList;
public List<FetchMKDataRequest.MKDataList> getMKDataList() {
if (mkDataList == null) {
mkDataList = new ArrayList<FetchMKDataRequest.MKDataList>();
}
return mkDataList;
}
public void setMkDataList(List<FetchMKDataRequest.MKDataList> mkDataList) {
this.mkDataList = mkDataList;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"functionalAreaSubGroup",
"personnelAreaCode"
})
public static class MKDataList {
#XmlElement(name = "FunctionalAreaSubGroup", required = true)
protected String functionalAreaSubGroup;
#XmlElement(name = "PersonnelAreaCode", required = true)
protected String personnelAreaCode;
public String getFunctionalAreaSubGroup() {
return functionalAreaSubGroup;
}
public void setFunctionalAreaSubGroup(String value) {
this.functionalAreaSubGroup = value;
}
public String getPersonnelAreaCode() {
return personnelAreaCode;
}
public void setPersonnelAreaCode(String value) {
this.personnelAreaCode = value;
}
}
}
package-info.java:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://www.example.com/fetchMKDataRequest.xsd", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.example.model.request;
This worked for me. You can define multiple namespaces in 'package-info' class for multiple request/response models.

Sending #Value annotated fields to a DTO layer returns null

I have a class which is composed of 2 different objects :
public class MyClass{
private OptionClass optionClass;
private ConstantClass constantClass;
public DocumentToSignRestRequest(OptionClass optionClass, ConstantClass constantClass) {
this.optionClass= optionClass;
this.constantClass= constantClass;
}
}
My first class is a classic POJO. My second class retrieve values from the application.properties file.
public class ConstantClass {
#Value("${api.url}")
private String hostName;
#Value("${sign.path}")
private String pathStart;
public ConstantClass () {
this.hostName= getHostName();
this.path = getPath();
}
I map MyClass with MyClassDto in order to call a service.
#PostMapping(
value="/sign",
consumes = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE },
produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }
)
public MyClassRest prepareDocument(#RequestBody DocumentToPrepare documentToPrepare) throws Exception {
MyClassRest returnValue = new MyClassRest ();
ModelMapper modelMapper = new ModelMapper();
MyClassDto myClassDto = modelMapper.map(documentToPrepare, MyClassDto .class);
DocumentDto signedDocument = documentService.signDocument(documentDto);
returnValue = modelMapper.map(signedDocument, DocumentRest.class);
return returnValue;
}
My DTO class work fine and retrieve the OptionClass datas, but concerning the second Class, i obtain null as value, while i try to print it out in the service layer.
Your ConstantClass should be a Bean or a Component (as #cassiomolin says in comments)
#Component
public class ConstantClass {
private String hostName;
private String pathStart;
public ConstantClass (#Value("${api.url}") String url, #Value("${sign.path}") String path ) {
this.hostName = url;
this.pathStart = path;
}
// getters...
Then you can easily inject this component in your Controller and use it.
#Controller
public class YourController(){
private ConstantClass constantClass;
public YourController(ConstantClass constantClass){
this.constantClass = constantClass;
}
#PostMapping("...")
public MyClass post(.....){
.....
MyClass myclass = new MyClass(this.constantClass,...)
.....
}
}
note that Spring can autowire #Value and #Component, ... via the constructor; that can be very useful when you do unit-testing

The resource identified by this request is able to generate responses only with features incompatible with the "accept" directive

I want to make a rest webservice call :
#RestController
#RequestMapping("stock")
public class StockController {
#Autowired
private StockService stockService;
#GetMapping(value = "/TOM", produces = "application/json")
public JsonModel getByLocAndItm(#RequestParam(required=false) String LOC, #RequestParam(required=false) String ITM) {
JsonModel jsonModel = new JsonModel();
List<com.java.oxalys.beans.Stock> stock = stockService.getByLocAndItm(LOC.split("|"), null);
jsonModel.setDatas(stock);
return jsonModel;
}
}
service :
#Service
public class StockServiceImpl implements StockService {
#Autowired
private StockDao stockDao;
#Override
public List<com.java.oxalys.beans.Stock> getByLocAndItm(String[] locs, String[] itms) {
return stockDao.getByLocAndItm(locs, itms);
}
}
DAO :
#Repository
public class StockDaoImpl implements StockDao {
#Override
public List<Stock> getByLocAndItm(String[] locs, String[] itms) {
List<Stock> ret = new ArrayList<Stock>();
String where = "";
String where_locs = "", sep_locs = "";
for(String loc : locs) {
where_locs += sep_locs + " s.LOC_0 = '" + loc + "'";
sep_locs = " or ";
}
where_locs = "(" + where_locs + ")";
where = where_locs;
Stock tmp = new Stock();
tmp.setLoc("eto");
tmp.setItmoxa(where);
ret.add(tmp);
return ret;
}
}
At runtime with postman : localhost:8080/Oxalys_WS/stock/TOM?LOC=BOQSCM171L then I get error The resource identified by this request is able to generate responses only with features incompatible with the "accept" directive present in the request header
So what is wrong ?

Resources