jersey #NotNull doesn't check on input - jersey

I have a jersey REST service, and I use #NotNull to check on the #pathparam, but it seems not working.I've include jersey-bean-validation in pom.xml
my code is here:
#Path("/resource")
public class MyResource extends AbstractResource {
#POST
#Path("/report")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ResponseClass(ReportAcknowledgement.class)
public Response getGrowthResults(MyRequest request,
#Context HttpHeaders headers) throws Exception {
String organizationId = request.getOrganization(); <-- null here
..
validateOrganization(organizationId);
public abstract class AbstractResource {
..
protected void validateOrganization(#NotNull(message = "{org.string.null}") #Valid String organizationId) throws Exception {
...
}

Ensure you are using the jersey-bean-validation dependency:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>2.22.2</version>
</dependency>
According to the documentation, having the above mentioned dependency on the classpath will activate the Bean Validation feature on Jersey.

The question is super-old, so you have probably already found an answer...
But you are missing the #Valid annotation on the parameter.
Try this
#POST
#Path("/report")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ResponseClass(ReportAcknowledgement.class)
public Response getGrowthResults(#javax.validation.Valid MyRequest request,
#Context HttpHeaders headers) throws Exception {
String organizationId = request.getOrganization(); <-- null here
..
validateOrganization(organizationId);
}

Related

Define a bean from external library

I have a poblem.
I am working with api binance and I add this library to use all functionalities.
<dependency>
<groupId>io.github.binance</groupId>
<artifactId>binance-connector-java</artifactId>
<version>1.3.0</version>
</dependency>
This is the code of external library
public class Market {
private final String baseUrl;
private final RequestHandler requestHandler;
private final boolean showLimitUsage;
public Market(String baseUrl, String apiKey, boolean showLimitUsage) {
this.baseUrl = baseUrl;
this.requestHandler = new RequestHandler(apiKey);
this.showLimitUsage = showLimitUsage;
}
When I try to inject this classe I always receive the following error on SpringBoot
Consider defining a bean of type 'com.binance.connector.client.impl.spot.Market' in your configuration.
I have an controller and I use like this.
#RestController
public class ExampleController {
#Autowired
private Market market;
#RequestMapping(value = "/ping", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<String> ping() {
return ResponseEntity.ok(market.ping());
}
}
I try to add the class with ComponentScan.
The code of external library is only for read.

Spring #RestController produces XML without namespaces

I have a #RestController which should return a result from a SOAP web service. The web service client classes are generated with maven-jaxb2-plugin and therefore using JAXB annotations.
#RestController
public class ZemisPersonSearchController {
#Autowired(required = true)
private SoapClient soapClient;
#RequestMapping(path = "/api/persons/{no}", produces = { MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.APPLICATION_XML_VALUE })
#PreAuthorize("hasRole('ROLE_GET_PERSON_DETAILS')")
public ResponseEntity<Object> getPersonDetails(HttpServletRequest httpReq, #PathVariable String no) {
Result result = soapClient.getPersonDetails(UUID.randomUUID().toString(), no);
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"responseHeader",
"getPersonDetailsResponse",
"searchPersonResponse",
"systemException"
})
#XmlRootElement(name = "result")
public class Result {
#XmlElement(name = "ResponseHeader")
protected ResponseHeaderType responseHeader;
#XmlElement(name = "GetPersonDetailsResponse")
protected PersonType getPersonDetailsResponse;
#XmlElement(name = "SearchPersonResponse")
protected SearchPersonResponseType searchPersonResponse;
#XmlElement(name = "SystemException")
protected FaultInfoType systemException;
...
As long as all works as expected, the result looks like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:result
xmlns:ns2="http://mynamespace/personsearchservice/v1">
<ns2:ResponseHeader>
...
But if there goes something wrong (i.e. soap endpoint isn't available) and an excpetion is thrown, the REST controller returns an 406 http status since the automatically generated response cannot be transformed to XML.
I've tried to extend my application with Jackson XML and registered the module to process JAXB annotations as suggested in documentations and blogs I found.
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</dependency>
#Bean
public Module jaxbModule() {
return new JaxbAnnotationModule();
}
But if I do so, the error for exceptions can now be generated as XML and I got the correct http status 500, but also the response when no error occurs contains no longer namespaces and it is important to keep the namespaces since it is a big and complex xml:
<result>
<ResponseHeader>
Does anybody have an idea what I have to do to get either the namespaces with jackson or the error transformed to xml with JAXB?
I found out, that spring creates a LinkedHashMap with error details automatically. When this map should be converted to html without jackson-xml on classpath, the http status 406 is given back due to a missing converter for the LinkedHashMap to html. So my solution is to add a simple AbstractHttpMessageConverter to my application which converts the map with error details to html. Therefore I don't need jackson-xml on the classpath and my XML is generated from JAXB with namespaces included.
The converter:
public class HttpXmlExceptionConverter extends AbstractHttpMessageConverter<Map<String, Object>> {
public HttpXmlExceptionConverter() {
super(MediaType.APPLICATION_XML, MediaType.TEXT_XML, new MediaType("application", "*+xml"));
}
#Override
protected boolean supports(Class<?> clazz) {
return Map.class.isAssignableFrom(clazz);
}
#Override
protected Map readInternal(Class<? extends Map<String, Object>> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
throw new NotImplementedException("readFromSource is not supported!");
}
#Override
protected void writeInternal(Map<String, Object> map, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
OutputStream s = outputMessage.getBody();
StringBuilder sb = new StringBuilder();
sb.append("<map>");
for (Entry<String, Object> entry : map.entrySet()) {
sb.append("<").append(entry.getKey()).append(">");
if (entry.getValue() != null)
sb.append(StringEscapeUtils.escapeXml(entry.getValue().toString()));
sb.append("</").append(entry.getKey()).append(">");
}
sb.append("</map>");
s.write(sb.toString().getBytes(Charset.defaultCharset()));
}
}
And register the converter:
#Configuration
//#EnableWebMvc
// -> EnableWebMvc is required if you don't want the spring boot auto configuration should be extended
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new HttpXmlExceptionConverter());
}
}

Swagger configured in Spring Boot shows only methods with POST and GET mapping

Swagger configured in Spring Boot shows only one method with POST mapping and one method with GET mapping from every controller. Swagger ignores another methods with GET and POST mapping and ignores all methods with PUT and DELETE mappings. My configuration:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("my.project.controllers"))
.paths(PathSelectors.ant("/api/*"))
.build();
}
}
Dependency in pom.xml:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
<scope>compile</scope>
</dependency>
My controllers code:
#RestController
#RequestMapping(value = "/api/users", produces = "application/json; charset=UTF-8")
public class UserController {
#Autowired
private UserService userService;
protected UserService getService() {
return userService;
}
#RequestMapping(method = GET)
public Page<User> query(#RequestParam Map<String, Object> parameters, Pageable pageable) {
return getService().query(parameters, pageable);
}
#ResponseStatus(CREATED)
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<User> create(#RequestBody User entity) {
return ResponseEntity.status(HttpStatus.CREATED).body(getService().create(entity));
}
#RequestMapping(value = "/{id:[0-9]+}", method = RequestMethod.PUT)
public ResponseEntity<User> update(#PathVariable Long id, #RequestBody User entity) {
return ResponseEntity.ok(getService().update(id, entity));
}
#RequestMapping("/current")
public ResponseEntity current() {
return ResponseEntity.ok(userService.getUser());
}
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "/{id:[0-9]+}/enable", method = RequestMethod.POST)
public void enable(#PathVariable("id") final long id) {
userService.enable(id);
}
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "/{id:[0-9]+}/disable", method = RequestMethod.POST)
public void disable(#PathVariable("id") final long id) {
userService.disable(id);
}
#RequestMapping(value = "/histories", method = RequestMethod.GET)
public List<UserHistory> histories() {
return userService.histories();
}
}
May be i need add some more configuration or add something else?
Based on your controller, I think you should add one more star in the path matcher in your swagger config:
.paths(PathSelectors.ant("/api/**"))
e.g /api/users/current would not be matched by the /api/* but by /api/**, and this why you are getting only the base path endpoints documented.

Mocked Spring #Service that has #Retryable annotations on methods fails with UnfinishedVerificationException

I'm using Spring Boot 1.4.0.RELEASE with spring-boot-starter-batch, spring-boot-starter-aop and spring-retry
I have a Spring Integration test that has a #Service which is mocked at runtime. I've noticed that if the #Service class contains any #Retryable annotations on its methods, then it appears to interfere with Mockito.verify(), I get a UnfinishedVerificationException. I presume this must be something to do with spring-aop? If I comment out all #Retryable annotations in the #Service then verify works ok again.
I have created a github project that demonstrates this issue.
It fails in sample.batch.MockBatchTestWithRetryVerificationFailures.batchTest() at validateMockitoUsage();
With something like:
12:05:36.554 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test method: context [DefaultTestContext#5ec0a365 testClass = MockBatchTestWithRetryVerificationFailures, testInstance = sample.batch.MockBatchTestWithRetryVerificationFailures#5abca1e0, testMethod = batchTest#MockBatchTestWithRetryVerificationFailures, testException = org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at sample.batch.service.MyRetryService$$FastClassBySpringCGLIB$$7573ce2a.invoke(<generated>)
Example of correct verification:
verify(mock).doSomething()
However I have another class (sample.batch.MockBatchTestWithNoRetryWorking.batchTest()) with a mocked #Service that doesn't have any #Retryable annotation and verify works fine.
What am I doing wrong?
In my pom.xml I have the following:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
...
Then all the related Java Classes
#SpringBootApplication
#EnableBatchProcessing
#Configuration
#EnableRetry
public class SampleBatchApplication {
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
#Autowired
private MyRetryService myRetryService;
#Autowired
private MyServiceNoRetry myServiceNoRetry;
#Bean
protected Tasklet tasklet() {
return new Tasklet() {
#Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext context) {
myServiceNoRetry.process();
myRetryService.process();
return RepeatStatus.FINISHED;
}
};
}
#Bean
public Job job() throws Exception {
return this.jobs.get("job").start(step1()).build();
}
#Bean
protected Step step1() throws Exception {
return this.steps.get("step1").tasklet(tasklet()).build();
}
public static void main(String[] args) throws Exception {
// System.exit is common for Batch applications since the exit code can be used to
// drive a workflow
System.exit(SpringApplication
.exit(SpringApplication.run(SampleBatchApplication.class, args)));
}
#Bean
ResourcelessTransactionManager transactionManager() {
return new ResourcelessTransactionManager();
}
#Bean
public JobRepository getJobRepo() throws Exception {
return new MapJobRepositoryFactoryBean(transactionManager()).getObject();
}
}
#Service
public class MyRetryService {
public static final Logger LOG = LoggerFactory.getLogger(MyRetryService.class);
#Retryable(maxAttempts = 5, include = RuntimeException.class, backoff = #Backoff(delay = 100, multiplier = 2))
public boolean process() {
double random = Math.random();
LOG.info("Running process, random value {}", random);
if (random > 0.2d) {
throw new RuntimeException("Random fail time!");
}
return true;
}
}
#Service
public class MyServiceNoRetry {
public static final Logger LOG = LoggerFactory.getLogger(MyServiceNoRetry.class);
public boolean process() {
LOG.info("Running process that doesn't do retry");
return true;
}
}
#ActiveProfiles("Test")
#ContextConfiguration(classes = {SampleBatchApplication.class, MockBatchTestWithNoRetryWorking.MockedRetryService.class}, loader = AnnotationConfigContextLoader.class)
#RunWith(SpringRunner.class)
public class MockBatchTestWithNoRetryWorking {
#Autowired
MyServiceNoRetry service;
#Test
public void batchTest() {
service.process();
verify(service).process();
validateMockitoUsage();
}
public static class MockedRetryService {
#Bean
#Primary
public MyServiceNoRetry myService() {
return mock(MyServiceNoRetry.class);
}
}
}
#ActiveProfiles("Test")
#ContextConfiguration(classes = { SampleBatchApplication.class,
MockBatchTestWithRetryVerificationFailures.MockedRetryService.class },
loader = AnnotationConfigContextLoader.class)
#RunWith(SpringRunner.class)
public class MockBatchTestWithRetryVerificationFailures {
#Autowired
MyRetryService service;
#Test
public void batchTest() {
service.process();
verify(service).process();
validateMockitoUsage();
}
public static class MockedRetryService {
#Bean
#Primary
public MyRetryService myRetryService() {
return mock(MyRetryService.class);
}
}
}
EDIT: Updated question and code based on a sample project I put together to show the problem.
So after looking at a similar github issue for spring-boot
I found that there is an extra proxy getting in the way. I found a nasty hack by unwrapping the aop class by hand, makes verification work, ie:
#Test
public void batchTest() throws Exception {
service.process();
if (service instanceof Advised) {
service = (MyRetryService) ((Advised) service).getTargetSource().getTarget();
}
verify(service).process();
validateMockitoUsage();
}
Hopefully, this can be fixed similar to the above github issue. I'll raise an issue and see how far I get.
EDIT: Raised the github issue
After I've seen #Joel Pearsons answer, and especially the linked GitHub issue, I worked around this by temporarily using a static helper method that unwraps and verifies:
public static <T> T unwrapAndVerify(T mock, VerificationMode mode) {
return ((T) Mockito.verify(AopTestUtils.getTargetObject(mock), mode));
}
With this method the only difference in the test cases is the verification call. There is no overhead other than this:
unwrapAndVerify(service, times(2)).process();
instead of
verify(service, times(2)).process();
Actually, it was even possible to name the helper method like the actual Mockito method, so that you only need to replace the import, but I didn't like the subsequent confusion.
However, unwrapping shouldn't be required if #MockBean is used instead of mock() to create the mocked bean. Spring Boot 1.4 supports this annotation.

how to get list of objects via requestbody in spring boot api

To get list of objects via #RequestBody in controller and process each object in a list to do a business logic.
I have tried this but not working
#RequestMapping(value="/updateservicetype", method=RequestMethod.POST,produces="application/json")
public #ResponseBody ServiceTypesMessage updateServiceType(#RequestBody List<BarberServiceType> serviceTypes,final HttpServletResponse response){
also tried following:
#RequestMapping(value="/updateservicetype", method=RequestMethod.POST,produces="application/json")
public #ResponseBody ServiceTypesMessage updateServiceType(#RequestBody BarberServiceType[] serviceTypes,final HttpServletResponse response){
Below works for me
#RequestMapping(value = "/payments", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody List<Payment> batchCreate(#RequestBody List<Payment> payments) {
return paymentService.create(payments);
}
You will need Jackson in the class path
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.0</version>
</dependency>
Json in put is
[{"sort":"10-20-30","account":"1234"},{"sort":"10-20-30","account":"1234"}]
You should use a wrapper class for your required list,
in another word, create a new class and name it "BarverServiceTypeRequest"
this class should contain your List:
public class BarberServiceTypeRequest{
private List<BarberServiceType> serviceTypes;
public List<BarberServiceType> getserviceTypes() {
return serviceTypes;
}
public void setServiceTypes(List<BarberServiceType>serviceTypes) {
this.date = date;
}
then your controller will be like:
#RequestMapping(value="/updateservicetype", method=RequestMethod.POST,produces="application/json")
public #ResponseBody ServiceTypesMessage updateServiceType(#RequestBody BarberServiceTypeRequest request, final HttpServletResponse response){
Your JSON object will be like:
{
"serviceTypes": [{"sort":"10-20-30","account":"1234"},{"sort":"10-20-30","account":"1234"}]
}
sure you can access your list with:
request.getserviceTypes();

Resources