I am building a webservices application using jersey,jax-rs
I have single jax-rs resource file at path "/authenticate"
I have multiple methods with individual paths like "/user" "/test"
#Path ("/authenticate")
public class Authenticate{
private static final Log log = LogFactory.getLog(Authenticate.class);
#QueryParam("entityId")
String entity;
#GET
#Path ("/{param}")
public Response getMsg(#PathParam ("param") String msg) {
String o = "Hello Welcome Back:"+msg;
return Response.status(200).entity(o).build();
}
#GET
#Path ("/user")
#Produces({"application/json"})
public UserDTO getUser (#Context HttpServletRequest request,
#QueryParam("userId") int userId) {
System.out.println("In Get User, User:"+userId);
System.out.println("In Get User, Entity:"+entity);
}
#GET
#Path ("/test")
#Produces({"application/json"})
public TestPOJO getTestPOJO () {
System.out.println("In Get TestPOJO");
System.out.println("In Get Test, Entity:"+entity);
return new TestPOJO();
}
}
As suggested for jersey client, I am using a single webresource from client and build subsequent webresources from the same webresource by using .path("/xxx").
Here is how I create the initial web resource
WebResource webResource = client.resource("http://localhost:8080/Service/jaxrs/authenticate");
webResource.queryParam("entityId", securityHelper.getEntityId().toString());
Here is how I use the webresource subsequently
MultivaluedMap<String, String> params = new MultivaluedMapImpl();
ClientResponse userRes = webResource.path("/user").queryParams(params).accept("application/json").get(ClientResponse.class);
I want to assign a queryparam to the initial webresource, and I want that to be retained by all subsequent webresources created using the .path(). But that is not happening right now. For example in the above code "entityId" is not available when the call with path("/user") is made.
My idea is to assign common parameters once and all subsequent users of the webResource need not add those again and again. Is there a way to do it? Will this approach work?
The line below creates a new WebResource and not changing the state of the webResource object:
webResource.queryParam("entityId", securityHelper.getEntityId().toString())
Eventually you could change your code like this to create the "base" resource:
WebResource webResource = client.resource("http://localhost:8080/Service/jaxrs/authenticate").queryParam("entityId", securityHelper.getEntityId().toString());
And then use this resource to create another resources as you like. WebResource.queryParam and WebResource.queryParams always create a new WebResource object.
I'm may not be the best person to answer this since I have entered the "world" of Jersey and RESTful servers not too long ago but since i saw this unanswered for 2 days ill try to help out as best as I can.
If i understood correctly you are trying to, by using a query, save the user information on entityId String so it will be available when you make a subsequent call.
Ok let's start with what you have. With your code (entityId as a global parameter), what you are specifying , is that when you are calling a resource from the Authenticate class, any call can be made with a query of the type '?entityId="something" and ANY method in this class can use the information sent in the query.
The thing is, for what I've learned by messing about with Jersey, whenever you make a call, the resource class (in your case Authenticate) is instantiated again. Therefor you can't just keep information in a global parameter since subsequent calls will have the String entityId as null.
This means that if you want to save the information you'll have to do it in a external resource (ex: db, file, etc). What method you choose depends on what you want to do and what are you looking for in your application.
I hope I was at least able to shed a sliver of light on your problem.
Related
Problem
I have a simple Spring Boot app with a basic RestController (full code available here). It consumes JSON and uses Jackson to convert request from JSON and response to JSON.
#RestController("/")
#RequestMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public class SomeController {
#Autowired
private SomeService someService;
#PostMapping
public ResponseEntity<SomeResponseDto> post(#RequestBody #Valid SomeRequestDto someRequestDto) {
final SomeResponseDto responseDto = new SomeResponseDto();
responseDto.setMessage(someRequestDto.getInputMessage());
responseDto.setUuid(someService.getUuid());
return ResponseEntity.ok(responseDto);
}
After start-up, the 1st request is about 10-times slower than any sub-sequent request. I debugged and profiled the app and it seems that on first request a Jackson JSON parser is getting initialized somewhere in AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters and AbstractJackson2HttpMessageConverter.
In sub-sequent requests, it seems to get re-used.
Question
How do I initialize Jackson JSON parsing during start-up so that also 1st request is fast?
I know how to trigger a method after Spring started. In PreloadComponent I added as an example how to do a REST request against the controller.
#Component
public class PreloadComponent implements ApplicationListener<ApplicationReadyEvent> {
private final Logger logger = LoggerFactory.getLogger(PreloadComponent.class);
#Autowired
private Environment environment;
#Autowired
private WebClient.Builder webClientBuilder;
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// uncomment following line to directly send a REST request on app start-up
// sendRestRequest();
}
private void sendRestRequest() {
final String serverPort = environment.getProperty("local.server.port");
final String baseUrl = "http://localhost:" + serverPort;
final String warmUpEndpoint = baseUrl + "/warmup";
logger.info("Sending REST request to force initialization of Jackson...");
final SomeResponseDto response = webClientBuilder.build().post()
.uri(warmUpEndpoint)
.header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.body(Mono.just(createSampleMessage()), SomeRequestDto.class)
.retrieve()
.bodyToMono(SomeResponseDto.class)
.timeout(Duration.ofSeconds(5))
.block();
logger.info("...done, response received: " + response.toString());
}
private SomeRequestDto createSampleMessage() {
final SomeRequestDto someRequestDto = new SomeRequestDto();
someRequestDto.setInputMessage("our input message");
return someRequestDto;
}
}
This only works in this toy example. In reality, I have many REST endpoints with complex DTOs and I would need to add a "warm-up" endpoint next to each "real" endpoint as I can't call my real endpoints.
What I already tried?
I added a second endpoint with a different DTO and called it in my PreloadComponent. This doesn't solve the problem. I assume that an Jackson / whatever instance is created for each type.
I autowired ObjectMapper into my PreloadComponent and parsed JSON to my DTO. Again, this doesn't solve the issue.
Full source available at: https://github.com/steinsag/warm-me-up
It turns out that Jackson validation is the problem. I added the JVM option
-verbose:class
to see when classes get loaded. I noticed that on 1st request, there are many Jackson validation classes getting loaded.
To confirm my assumption, I re-worked my example and added another independent warm-up controller with a distinct DTO.
This DTO uses all Java validation annotations also present like in the real DTO, e.g. #NotNull, #Min, etc. In addition, it also has a custom enum to also have validation of sub-types.
During start-up, I now do a REST request to this warm-up endpoint, which doesn't need to contain any business logic.
After start-up, my 1st request is now only 2-3 times slower than any sub-sequent requests. This is is acceptable. Before, the 1st request was 20-40 times slower.
I also evaluated if really a REST request is needed or if it is sufficient to just do JSON parsing or validation of a DTO (see PreloadComponent). This reduces runtime of 1st request a bit, but it is still 5-15 times slower than with proper warm-up. So I guess a REST request is needed to also load other classes in Spring Dispatcher, etc.
I updated my example at: https://github.com/steinsag/warm-me-up
I believe, that a lot of classes will be lazy-loaded. If first call performance is important, then I think warming up by calling each endpoint is the way to go.
Why do you say, that you cannot call the endpoints? If you have a database and you don't want to change the data, wrap everything in a transaction and roll it back after the warm up calls.
I haven't seen any other method to solve this, which doesn't necessarily mean, that it doesn't exist ;)
I'd like to create URLs based on the URL used by the client for the active request. Is there anything smarter than taking the current HttpServletRequest object and it's getParameter...() methods to rebuilt the complete URL including (and only) it's GET parameters.
Clarification: If possible I want to resign from using a HttpServletRequest object.
Well there are two methods to access this data easier, but the interface doesn't offer the possibility to get the whole URL with one call. You have to build it manually:
public static String makeUrl(HttpServletRequest request)
{
return request.getRequestURL().toString() + "?" + request.getQueryString();
}
I don't know about a way to do this with any Spring MVC facilities.
If you want to access the current Request without passing it everywhere you will have to add a listener in the web.xml:
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
And then use this to get the request bound to the current Thread:
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()
Instead of using RequestContextHolder directly, you can also use ServletUriComponentsBuilder and its static methods:
ServletUriComponentsBuilder.fromCurrentContextPath()
ServletUriComponentsBuilder.fromCurrentServletMapping()
ServletUriComponentsBuilder.fromCurrentRequestUri()
ServletUriComponentsBuilder.fromCurrentRequest()
They use RequestContextHolder under the hood, but provide additional flexibility to build new URLs using the capabilities of UriComponentsBuilder.
Example:
ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentRequestUri();
builder.scheme("https");
builder.replaceQueryParam("someBoolean", false);
URI newUri = builder.build().toUri();
Java's URI Class can help you out of this:
public static String getCurrentUrl(HttpServletRequest request){
URL url = new URL(request.getRequestURL().toString());
String host = url.getHost();
String userInfo = url.getUserInfo();
String scheme = url.getProtocol();
String port = url.getPort();
String path = request.getAttribute("javax.servlet.forward.request_uri");
String query = request.getAttribute("javax.servlet.forward.query_string");
URI uri = new URI(scheme,userInfo,host,port,path,query,null)
return uri.toString();
}
in jsp file:
request.getAttribute("javax.servlet.forward.request_uri")
You can also add a UriComponentsBuilder to the method signature of your controller method. Spring will inject an instance of the builder created from the current request.
#GetMapping
public ResponseEntity<MyResponse> doSomething(UriComponentsBuilder uriComponentsBuilder) {
URI someNewUriBasedOnCurrentRequest = uriComponentsBuilder
.replacePath(null)
.replaceQuery(null)
.pathSegment("some", "new", "path")
.build().toUri();
//...
}
Using the builder you can directly start creating URIs based on the current request e.g. modify path segments.
See also UriComponentsBuilderMethodArgumentResolver
If you need the URL till hostname and not the path use Apache's Common Lib StringUtil, and from URL extract the substring till third indexOf /.
public static String getURL(HttpServletRequest request){
String fullURL = request.getRequestURL().toString();
return fullURL.substring(0,StringUtils.ordinalIndexOf(fullURL, "/", 3));
}
Example: If fullURL is https://example.com/path/after/url/ then
Output will be https://example.com
System.out.println(((HttpServletRequest)request).getRequestURI());
I used it. hope it's useful.
By default when we have a repository with save method exposed we can do a PATCH request. Then Spring Data REST retrieves the original object from the database and apply changes to entity and then saves it for us (inside JsonPatchHandler class). This allows us to do the following request for class
class Address {
Long id;
String street;
Long houseNumber;
}
PATCH /api/addresses/1 with body
{ houseNumber: 123 }
And only this one field will be changed.
Now having custom controller we would like to in the update method receive the whole object (after HATEOAS merged it with the original object from the DB)
#RepositoryRestController
#ExposesResourceFor(Address.class)
#ResponseBody
#RequestMapping("/addresses")
public class AdddressController {
#PatchMapping("/{addressId}")
public Resource<Address> update(#RequestBody Resource<Address> addressResource, #PathVariable Long addressId) {
Address address= addressResource.getContent();
// .... some logic
address = addressRepository.save(address);
return new Resource<>(address);
}
}
Unfortunately in the place where I would do some logic I get the Address with null fields instead of the merged object.
Is it possible to plug the custom controller in the Spring Data REST stack so that when patching the request it will merge it for me (as it does for normal repositories)?
edit:
I would like to find a solution that works transparently both with PATCH(content-type:application/json-patch+json) and PATCH(content-type: application/hal+json)
After browsing the Spring sources I haven't found a reasonable solution. As a result I've created issue in their - JIRA
For the moment the only reasonable workaround is following - create custom controller that has PersitentEntityResource as a parameter and has both {id} and {repository} placeholders in its path i.e.
#PatchMapping("/addresses/{id}/{repository}")
public Resource<Address> update(PersistentEntityResource addressResource) {
...
}
which makes the invocation endpoint /addresses/123/addresses
My Spring REST API is decorated as follows:
In below, I am confused weather, parameters such as list, operation need to be part of Url as query string or do they need to be part of Request Body as form data (Url encoded).
There are situations where I am sending these parameters in query string and it works fine. But couple of my api's are not working properly on production and they work only if I send the data in request body as Url encoded. Can anyone help me explain this behaviour ?
#RequestMapping(value = "/bulkupdate/{companyId}", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<String> bulkupdateArticle(#RequestParam("list") String documentIdList,
#PathVariable("companyId") String companyId, #RequestParam("operation") String operation){
try{
Looking at the resource I find that it could be better designed in a more REST-ful fashion. I don't like to see POSTed data in the reside in the url.
Next to becoming more Rest-ful it would also make live for you much easier.
I would create a Data Transfer Object and pass it as the body of the POST request to your resource/spring controller.
Going from your data:
public class ArticleToUpdate {
private String list; // list of what ? Maybe design it like List<String> somethingMoreMeaningFull
private String operation;
// .. getters
}
public ResponseEntity<String> bulkupdateArticle(#RequestBody ArticleToUpdate articleToUpdate) {
// .. do whatever you need with the posted data
Now you can post a JSON or XML document in the body which will probably life much easier.
Additionally you could also add validation on the posted data through #Valid support now.
At the moment I am bussy with implementing a new url structure for our webshop. The new url structure should be more optimized for search engines. We also want that our old structure will still be working and will use a 301 to redirect to a the new structure.
The problem is: the new structure sometimes conflicts with the old urls.
Example of the old url mapping:
#RequestMapping(value = "/brand/{categoryCode}/{categoryName}/{brandGroup}.do", method = RequestMethod.GET)
New structure:
#RequestMapping(value = "/brand/{brandGroup}/{superCategoryName}/{categoryName}.do", method = RequestMethod.GET)
As you can see the url's have the same amount of values, so the old mapping will catch the new one and vice versa.
What is the best way to fix this? Using a url filter to rewrite the old ones to the new url structure?
You could use an URL router in Spring MVC; you can define conflicting routes within your app and handle them with route prorities (first route to match the request wins) and refine request matching.
Your routes configuration file could look like:
GET /brand/{<[0-9]+>categoryCode}/{categoryName}/{brandGroup}.do oldcontroller.oldAction
GET /brand/{<[a-zA-Z]+>brandGroup}/{superCategoryName}/{categoryName}.do newController.newAction
In spring boot, regular expressions can be used when mapping the #PathVariable, and this can be useful to resolve url conflicts:
#RestController
public class TestController {
#PutMapping("/test/{id:^[1-9][0-9]*}") // id must be a number greater that 1
public void method1(#PathVariable long id, #RequestBody DataDto1 data) {
}
#PutMapping("/test/foo")
public void method1(#Valid #RequestBody DataDto2 data) {
}
}