I am new to Jersey and trying to convert a project from Spring MVC into Jersey. With my current build however, all requests return a resource not available error. Any help would be greatly appreciated.
My Dependencies:
dependencies {
compile('org.springframework.boot:spring-boot-starter')
compile("org.springframework.boot:spring-boot-starter-data-jpa:1.4.0.RELEASE")
compile("org.springframework.boot:spring-boot-starter-jersey")
runtime('org.hsqldb:hsqldb')
compileOnly("org.springframework.boot:spring-boot-starter-tomcat")
testCompile('org.springframework.boot:spring-boot-starter-test')
}
My Jersey Config
#Configuration
public class JersyConfig extends ResourceConfig {
public JersyConfig() {
registerEndpoints();
configureSwagger();
}
private void configureSwagger() {
register(ApiListingResource.class);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.0");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8090");
beanConfig.setBasePath("/");
beanConfig.setResourcePackage(OwnerController.class.getPackage().getName());
beanConfig.setPrettyPrint(true);
beanConfig.setScan(true);
}
private void registerEndpoints() {
register(OwnerController.class);
}
}
#Api(value = "Owner controller", tags = {"Owner resource"})
public class OwnerController {
private final ClinicService clinicService;
#Autowired
public OwnerController(ClinicService clinicService) {
this.clinicService = clinicService;
}
#GET
#Path("/{ownerId}")
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "get owner by id", response = Owner.class)
public Response getOwner(
#ApiParam(name = "owner id", value = "owner id that must be fetched") #PathParam("ownerId") int id ) {
Owner owner = clinicService.findOwnerById(id);
return Response.status(200).entity(owner).build();
}
#GET
#Path("/owners")
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "get all owners", response = Owner.class, responseContainer = "List")
public Response getOwners() {
List<Owner> owner = (List<Owner>) clinicService.findAllOwners();
return Response.status(200).entity(owner).build();
}
}
Register your package which contains jersey resources using packages() method in JerseryConfig() constructor -
public JersyConfig() {
packages("PACKAGE_CONTAINING_JERSEY_RESOURCES");
registerEndpoints();
configureSwagger();
}
Related
I have a problem with my test Spring Boot app. It works just fine, but when I enable Spring validation by adding dependency etc and adding a #Configuration:
#Configuration
public class TestConfiguration {
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
I get 404 for my test endpoint.
{
"timestamp": 1601507037178,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/test"
}
I've already applied some solutions/proposals from similar problems (eg here or here) but without a success.
Here is my code:
https://github.com/zolv/error-handling-test
API interface:
#Validated
public interface TestApi {
#PostMapping(
value = "/test",
produces = {"application/json"},
consumes = {"application/json"})
#ResponseBody
ResponseEntity<TestEntity> getTest(#Valid #RequestBody(required = false) TestEntity request);
}
TestEntity just to send something:
#Data
public class TestEntity {
#JsonProperty("test")
#NotNull
private String test;
}
Controller implementation:
#RestController
#RequiredArgsConstructor
#Validated
public class TestController implements TestApi {
#Override
#ResponseBody
public ResponseEntity<TestEntity> getTest(#Valid #RequestBody TestEntity request) {
return ResponseEntity.ok(request);
}
}
My controller advice:
#ControllerAdvice
public class DefaultErrorHandlerAdvice extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {ConstraintViolationException.class})
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public ResponseEntity<String> handleValidationFailure(ConstraintViolationException ex) {
StringBuilder messages = new StringBuilder();
for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
messages.append(violation.getMessage());
}
return ResponseEntity.badRequest().body(messages.toString());
}
#Override
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_PROBLEM_JSON)
.body("problem");
}
}
Application:
#SpringBootApplication
#EnableWebMvc
#ComponentScan(basePackages = "com.test")
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
A test I use, but it fails also using Postman:
#SpringJUnitConfig
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class TestControllerTest {
#Autowired protected TestRestTemplate restTemplate;
#Test
void registrationHappyPath() throws Exception {
/*
* Given
*/
final TestEntity request = new TestEntity();
/*
* When/
*/
final ResponseEntity<String> response =
restTemplate.postForEntity("/test", request, String.class);
/*
* Then
*/
Assertions.assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
final String body = response.getBody();
Assertions.assertNotNull(body);
}
}
If I comment out a TestConfiguration then everything works fine.
Thank You in advance for any help.
You should set MethodValidationPostProcessor#setProxyTargetClass(true) because by default MethodValidationPostProcessor uses JDK proxy which leads to loss of your controller in the Spring context.
When AbstractHandlerMethodMapping#processCandidateBean is called isHandler(Class<?> beanType) will return false because JDK proxy doesn't contain #RestController annotation.
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor mvProcessor = new MethodValidationPostProcessor();
mvProcessor.setProxyTargetClass(true);
return mvProcessor;
}
I have a JAX-RS web service which I want to document with Swagger 2.1.
The config is build in my Servlet:
public class FooWebservice extends HttpServlet {
#Override
public void init(ServletConfig config) throws ServletException {
OpenAPI oas = new OpenAPI();
Info info = new Info()
.title("Foo-Webservice")
.version("1.0.0");
oas.info(info);
SwaggerConfiguration oasConfig = new SwaggerConfiguration()
.prettyPrint(true)
.openAPI(oas)
.resourcePackages(Stream.of("de.kembytes.foo.webservice.controller").collect(Collectors.toSet()));
try {
new JaxrsOpenApiContextBuilder()
.servletConfig(config)
.openApiConfiguration(oasConfig)
.buildContext(true);
} catch (OpenApiConfigurationException e) {
throw new ServletException(e.getMessage(), e);
}
}
}
In addition I have a controller that defines the operation (in package de.kembytes.foo.webservice.controller):
#Path("/foo")
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Tag(name = "Foo")
public class FooController {
#POST
#Path("/calculate")
#Operation(summary = "returns bar",
responses = {
#ApiResponse(responseCode = "200", description = "bar", content = {
#Content(mediaType = MediaType.APPLICATION_XML, schema = #Schema(implementation = Bar.class)),
#Content(mediaType = MediaType.APPLICATION_JSON, schema = #Schema(implementation = Bar.class)) }) })
public Bar calculate(#RequestBody(required = true, content = #Content(schema = #Schema(implementation = FooInput.class))) FooInput input) throws Exception {
Bar bar = new Bar();
bar.setValue1(...);
bar.setValue2(...);
bar.setValue3(...);
return bar;
}
}
When i start my Application and get the OpenApi-Documentation the FooController is not included. It looks like this:
{
"openapi" : "3.0.1",
"info" : {
"title" : "Foo-Webservice",
"version" : "1.0.0"
}
}
Why is it that the config was not loaded in FooController although it is in the specified resource package?
I have solved this problem by scanning my package for all classes with the annotation #Path with the Reflections library.
Then i set all of them as resourceClasses.
The init method looks like this now:
#Override
public void init(ServletConfig config) throws ServletException {
OpenAPI oas = new OpenAPI();
Info info = new Info()
.title("Foo-Webservice")
.version("1.0.0");
oas.info(info);
Set<String> resourceClasses = new Reflections(getClass().getPackageName())
.getTypesAnnotatedWith(Path.class)
.stream().map(c -> c.getName())
.collect(Collectors.toSet());
SwaggerConfiguration oasConfig = new SwaggerConfiguration()
.prettyPrint(true)
.openAPI(oas)
.resourceClasses(resourceClasses);
try {
new JaxrsOpenApiContextBuilder()
.servletConfig(config)
.openApiConfiguration(oasConfig)
.buildContext(true);
} catch (OpenApiConfigurationException e) {
throw new ServletException(e.getMessage(), e);
}
}
I have a couple of APIs and using springfox-swagger for API documentation.
I have a requirement to add the creation date to the respective API. How can I achieve this using swagger. I don't need any API versioning.
Ex:
#ApiOperation(value = "Creates a new user and returns the created user.")
#PostMapping(/user)
public ResponseEntity<UserDto> createUser(#RequestBody UserDto userDto) {
User user =userService.create(userDto);
return new ResponseEntity<>(UserMappers.USER_ENTITY_TO_DTO.apply(user),HttpStatus.CREATED);
}
In the above example, I want to add the creation date of /user so that I can trace the creation date.
In my project I have a similar requirement. As a solution I have created a custom annotation (for marking the endpoint) and wrote a plugin (for updating the API description).
Option #1
#ApiSince annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ApiSince {
String value() default "";
}
ApiSincePlugin plugin:
#Component
public class ApiSincePlugin implements OperationBuilderPlugin {
private final DescriptionResolver resolver;
#Autowired
public ApiSincePlugin(DescriptionResolver resolver) {
this.resolver = resolver;
}
#Override
public void apply(OperationContext context) {
final String sinceTemplate = "### Since %s%n%n%s";
String notes = "";
Optional<ApiOperation> apiOperationOptional = context.findAnnotation(ApiOperation.class);
if (apiOperationOptional.isPresent()) {
notes = apiOperationOptional.get().notes();
}
String finalNotes = notes;
Optional<ApiSince> apiSinceOptional = context.findAnnotation(ApiSince.class);
if (apiSinceOptional.isPresent()) {
finalNotes = String.format(sinceTemplate, apiSinceOptional.get().value(), notes);
}
context.operationBuilder().notes(resolver.resolve(finalNotes));
}
#Override
public boolean supports(DocumentationType type) {
return true;
}
}
#ApiSince in action:
#ApiSince(value = "2019-10-31")
#PostMapping(value = "/login")
#ApiOperation(value = "Authenticate user", nickname = "login", notes = "your API description")
#ResponseStatus(HttpStatus.OK)
#ApiResponses(value = {
#ApiResponse(code = 200, response = LoginResponse.class, message = HTTP_200_OK),
...
})
#ResponseBody
ResponseEntity<LoginResponse> login(...);
If you don't want do add it in the description but as an extra JSON attribute then take a look at this solution: Custom Operation Builder Plugin
.
Option #2
#ApiSince annotation (code same as above)
ApiSincePlugin plugin:
#Component
public class ApiSincePlugin implements OperationBuilderPlugin {
#Override
public void apply(OperationContext context) {
Optional<ApiSince> annotation = context.findAnnotation(ApiSince.class);
if (annotation.isPresent()) {
String value = annotation.get().value();
ObjectVendorExtension extention = new ObjectVendorExtension("x-since");
extention.addProperty(new StringVendorExtension("value", value));
context.operationBuilder().extensions(Collections.singletonList(extention));
}
}
#Override
public boolean supports(DocumentationType documentationType) {
return true;
}
}
Activate extensions in the Swagger UI:
#Bean
UiConfiguration uiConfig() {
return UiConfigurationBuilder
.builder()
.showExtensions(true)
...
.build();
}
#ApiSince in action (code same as above):
Hi I've referred to this link for consuming a SOAP webservice.
But i'm not sure how to call the client method.
Please find my code below :
ClientConfig.java
package com.exclusively.unicommerce.service;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
#Configuration
public class ClientConfig {
#Bean
public Jaxb2Marshaller marshaller()
{
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.unicommerce.wsdl");
return marshaller;
}
#Bean
public SaleOrderClient saleorderclient(Jaxb2Marshaller marshaller) {
SaleOrderClient client = new SaleOrderClient();
client.setDefaultUri("https://link.com/services/soap/?version=1.6");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
SaleOrderClient.java
public class SaleOrderClient extends WebServiceGatewaySupport{
private static final String uri = "https://link.com/services/soap/?version=1.6";
public String createSaleOrder(Suborder suborder)
{
SaleOrder saleorder = new SaleOrder();
saleorder = setSaleOrderObject(suborder);
CreateSaleOrderRequest request = new CreateSaleOrderRequest();
request.setSaleOrder(saleorder);
//PLEASE NOTE THIS Line of CODE.
this.getWebServiceTemplate().marshalSendAndReceive(uri,request);
return "Pushed to Unicommerce";
}
public SaleOrder setSaleOrderObject(Suborder suborder)
{
SaleOrder saleorder = new SaleOrder();
saleorder.setAdditionalInfo(null);
saleorder.setAddresses(null);
saleorder.setCashOnDelivery(null);
saleorder.setCFormProvided(null);
saleorder.setChannel(null);
saleorder.setCode(null);
saleorder.setCurrencyCode(null);
saleorder.setCustomerCode(null);
saleorder.setDisplayOrderCode(null);
saleorder.setNotificationEmail(null);
saleorder.setNotificationMobile(null);
saleorder.setVerificationRequired(null);
return saleorder;
}
}
SuborderController.java
#Controller
public class SuborderController {
private String currentStatus, finalStatus,status,response;
#Autowired
private SuborderService suborderservice;
#RequestMapping(value = "/orders/add", method = RequestMethod.POST)
#ResponseBody
public String addOrders(#RequestBody Suborder order) {
if(order.getSuborderId() == null || order.getSuborderId().isEmpty())
return "BAD REQUEST";
suborderservice.addOrders(order);
//**CALL To createSaleorder(order)**
//response = saleorderclient.createSaleorder(order);
return response;
}
Here things to note is that webservice has provided request class but no response class. Second i tried
#Autowired
SaleOrderClient saleorderclient;
But it threw bean not found exception.
I'm not able to understand how do i access this method.
Please help. TIA.
I resolved my issue by adding below mentioned lines in SuborderController.java
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ClientConfig.class);
ctx.refresh();
SaleOrderClient saleorderclient = ctx.getBean(SaleOrderClient.class);
I am using Retrofit for post request to Spring Boot service but always is called failure Callback method. This is my simplified code:
Spring Boot service (Controller):
#RestController
public class ServController {
#Autowired
private UserRepository userRepository;
#RequestMapping(value = "/user", method = RequestMethod.POST)
public Boolean signUpUser(#RequestBody User user)
{
return true;
}
}
My client interface:
public interface ChainApi {
public static final String USER_PATH = "/user";
#POST(USER_PATH)
public void signUpUser(#Body User user, Callback<Boolean> callback);
}
Async POST request:
User user = new User();
user.setId(12);
user.setName(nameEtx.getText().toString());
user.setEmail(emailEtx.getText().toString());
user.setPassword(passwordEtx.getText().toString());
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constant.URL_LOCALHOST)
.build();
ChainApi service = restAdapter.create(ChainApi.class);
service.signUpUser(user, new Callback<Boolean>() {
#Override
public void success(Boolean aBoolean, Response response) {
Log.i(TAG, "Succesfull");
#Override
public void failure(RetrofitError error) {
Log.i(TAG, "Error " + error.getMessage()); // 400 Bad Request
}
});
This is my User class(POJO):
#JsonIgnoreProperties(value = { "additionalProperties"})
public class User {
#JsonProperty("id")
private Integer id;
#JsonProperty("name")
private String name;
#JsonProperty("password")
private String password;
#JsonProperty("email")
private String email;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
// methods
}
NOTES: I am developing in Android and when make manual POST request from Postman I get 200 OK.
In addition I get in logcat message: 400 Bad Request
Retrofit uses GSON as its default Converter. #JsonIgnoreProperties is an annotation from Jackson. Looking at your RestAdapter you don't seem to be specifying a Jackson Converter.
Square has implemented a JasksonConverter, you use it by including the dependency.
compile 'com.squareup.retrofit:converter-jackson:X.X.X'
Use the version that matches your Retrofit version.
Then
JacksonConverter converter = JacksonConverter(new ObjectMapper());
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constant.URL_LOCALHOST)
.setConverter(converter)
.build();