I'm using Jetty to implement JSR-356 Web socket for chat application.
For managed conversations and sending different message types I'm using DTO object which are serialized from/to json using Jackson and TextDecoder
public class JsonDtoCodec implements Decoder.Text<Dto>,Encoder.Text<Dto>{
ObjectMapper om = ... //some object mapper
#Override
public Dto decode(String json) throws DecodeException {
Dto dto = null;
try {
dto = om.readValue(json,Dto.class);
} catch (IOException e) {
//ToDo add Log
}
return dto;
}
#Override
public String encode(Dto dto) throws EncodeException {
try {
return om.writeValueAsString(dto);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Now, In my endpoint I'm using direct Dto objects:
#ServerEndpoint(value = "/{version}/{userType}/{token}",
decoders =JsonDtoCodec.class,
encoders = JsonDtoCodec.class)
public class ChatServerEndPoint {
#OnMessage
public void onMessage(final #PathParam("userType") String userType,
final #PathParam("token") String token,
final #PathParam("version") String version,
final Session session,
Dto dto)
throws IOException {
//handling all DTO related object and sending response
Dto response = .... // create some DTO
session.getAsyncRemote().sendObject(dto);
}
}
Unfortunately, I need to handle uploading image files through web socket up to 100Kb size.
Can I use the same endpoint for handling both, DTOs and Binary Data?
If not should I add new EndPoint and should I handle two separate connections one for binaries and one for Json content?
You most certainly can.
You would have 2 #OnMessage annotated methods. (one handling TEXT, the other BINARY)
Assuming that your JsonToDtoCodec extends Decoder.Text<Dto>, then your other method would be declared as ...
#OnMessage(maxMessageSize=100000)
public void onFileUpload(final Session session, final ByteBuffer buf)
{
}
Since you said the files would be a maximum of 100kb, then having the method pass in the complete ByteBuffer is sufficient. If it were larger, you could use an InputStream approach instead. As for limiting the size of that buffer, your maxMessageSize annotation would keep that sane.
Note: if a client attempts to send something over 100kb in size on binary, then the connection will be closed with close code 1009: Too Big.
Related
I am currently working with the Restlets framework, and I cannot find a way to manually set the HTTP response code within a service method. Consider the following snippet of code:
public class MyResource extends ServerResource {
#Post("json")
public Representation doSomething(Representation entity) throws IOException {
int status = 200;
try {
// do something which might throw an exception
}
catch (Exception e) {
// log the exception
// *** I would like to assign HTTP status 500 here ***
status = 500;
}
JSONObject responseJSON = new JSONObject();
responseJSON.put("result", "some data");
Representation rep = new JsonRepresentation(responseJSON.toJSONString());
return rep;
}
}
I have the ability to catch and log an exception, should one occur, but it is not clear how I can change the HTTP response code. As far as I know, returning from doSomething will automatically be handled by Restlets with an 200 HTTP response code.
I know how to assign the status code directly from a filter or servlet, but is it possible to do this within Restlets, without going down the servlet layer?
As far as I know, there is an object called ResponseEntity which you can use to operate with microservices and a request-response programming model, which allows you to specify the returning HTTP return code. However, you need entities for this, and I think this goes below your abstraction level of Servlets.
You can change them to some predefined values such as HTTP.INTERNAL_SERVER_ERROR and such, which translate to a value in the end, which you can Google in the end.
I hope this was of some help
EDIT:
Import the necessary resource for a ResponseEntity object. In STS, it is
import org.springframework.http.ReponseEntity;
import org.springframework.http.HttpStatus;
public class MyResource extends ServerResource {
#Post("json")
public ResponseEntity<Representation> doSomething(Representation entity) throws IOException {
int status = 200;
try {
// do something which might throw an exception
}
catch (Exception e) {
ResponseEntity<Representation> response = null;
response = new ResponseEntity<Representation>(HttpStatus.INTERNAL_SERVER_ERROR);
return response;
}
JSONObject responseJSON = new JSONObject();
responseJSON.put("result", "some data");
Representation rep = new JsonRepresentation(responseJSON.toJSONString());
return rep;
}
And sorry for the delay. I am new to Stack Overflow
This question is a result of some work I'm doing with the Spring Security Oauth2 library. I've set up an oauth2 authorization server and an oauth2 resource server, the latter of which is meant to authorize based on access tokens.
The problem is that normally access tokens are passed in a header, but the big client we're setting this up for wants to pass the access token in a JSON request body. There's an interface you can use to set up custom access token extraction, but it looks like this:
public interface TokenExtractor {
/**
* Extract a token value from an incoming request without authentication.
*
* #param request the current ServletRequest
* #return an authentication token whose principal is an access token (or null if there is none)
*/
Authentication extract(HttpServletRequest request);
}
So, as best I can tell, all I have access to is the raw HTTPServletRequest, from which I need to deserialize the request and extract the access token.
Further complicating things, though, is the fact that the request body also contains other parameters needed for processing, so I want to deserialize it to a DTO class that I pass into my controller, something like so:
#RequestMapping("/oauth/someresource")
#Transactional
public Map<String, String> resource(#AuthenticationPrincipal UserDetails userDetails,
#RequestBody ClientRequestDto clientRequestDto) {
// Do some processing based on the request dto
}
I tried manually deserializing the request in the token extractor, but then I get an error "java.lang.IllegalStateException: getReader() has already been called for this request".
I was brainstorming a few possible solutions that I could research, and so far I've come up with:
find a way to reset the input stream
deserialize the object in the Token Extractor, attach it to the raw request object, and just access the raw request object in my controller instead of using #RequestBody
like 2, but find a way to add a custom deserializer that fetches the object attached to the raw request instead of processing the request's input stream.
Anyways, those are just some thoughts, if anyone has any ideas in terms of an elegant way of solving this, I'd greatly appreciate it.
EDIT: I did find this question which is similar: Spring reading request body twice, and the last answer did have one possible solution (creating a decorator request class that allows multiple input stream reads and creating a filter early on in the filter chain that wraps the HttpServletRequest). It seems workable, but a little heavy duty, so I'll leave this up to see if anyone has any other ideas as well.
So I ended up finding yet another question that addressed this issue that I didn't see before posting (How can I read request body multiple times in Spring 'HandlerMethodArgumentResolver'?). That one also suggested creating a decorator around the HttpServletRequest, so I adapted the info from http://www.myjavarecipes.com/how-to-read-post-request-data-twice-in-spring/, adding a protection against large requests.
Here's what I came up with, in case anyone has any feedback:
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
// We include a max byte size to protect against malicious requests, since this all has to be read into memory
public static final Integer MAX_BYTE_SIZE = 1_048_576; // 1 MB
private String _body;
public MultiReadHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
_body = "";
InputStream bounded = new BoundedInputStream(request.getInputStream(), MAX_BYTE_SIZE);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(bounded));
String line;
while ((line = bufferedReader.readLine()) != null){
_body += line;
}
}
#Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(_body.getBytes());
return new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
#Override
public boolean isFinished() {
return byteArrayInputStream.available() == 0;
}
#Override
public boolean isReady() {
return true;
}
#Override
public void setReadListener(ReadListener readListener) {
}
};
}
#Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
I used the following configuration:
#Bean
FilterRegistrationBean multiReadFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
MultiReadRequestFilter multiReadRequestFilter = new MultiReadRequestFilter();
registrationBean.setFilter(multiReadRequestFilter);
registrationBean.setOrder(SecurityProperties.DEFAULT_FILTER_ORDER - 2);
registrationBean.setUrlPatterns(Sets.newHashSet("/path/here"));
return registrationBean;
}
I have some JAX-RS web services that takes JSON input from clients, and return JSON outputs to clients. I need to log both the input and output JSON messages. I know how to do so for inputs, as shown in code below. This code will grab the exact JSON input that is coming in. How do I do the same for the outputs?
public class LogRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
InputStream inputStream = requestContext.getEntityStream();
// Read the JSON request input from the input stream.
}
}
Use ContainerResponseFilter to get the exchanged Entity. Add the filter to the provider list of the JAX-RS server
public class LogResponseFilter implements ContainerResponseFilter {
public void filter(ContainerRequestContext inContext, ContainerResponseContext outContext) throws IOException{
Object entity = outContext.getEntity();
//log entity.toSgring()
//You can use the output stream to write a custom message
//OutputStream outputStream = outContext.getEntityStream();
}
}
To just log the payload, looking at this link https://stackoverflow.com/a/25337892/6371459 I see
The OutputStream is empty at the time the Filter is called because the JAX-RS runtime has not written to it. After your Filter the runtime will choose the correct MessageBodyWriter which will serialize the entity to the OutputStream
So it is needed to add a WriterInterceptor to be execuded after MessageBodyWriters write to desired content-type.
In the previous link you have the full code.
I'm a Jersey newbie and I need to log the JSON response. I wish to take the entity and convert it to JSON exactly as done by the Jersey framework does (same mapper, etc.). Is there a way to extract its mapper (and call, for example, its writeValueAsString)?
You don't specify which package you use for producing the JSON response (neither you explain much about your jersey server), but I will assume you use Jackson.
You have some tracing in Jersey, take a look here, but as fas as I know it does not do what you need.
But first of all you should implement a LoggingFilter,
public class YourLoggingFilter implements ContainerResponseFilter {
#Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
throws IOException {
...
}
}
And I assume you extend the ResourceConfig class to add resources, you need to add the new filter here:
public class YourApplication extends ResourceConfig {
public YourApplication() {
super();
register(YourAuthenticationRequestFilter.class, Priorities.AUTHENTICATION);
register(YourExceptionMapper.class);
register(YourLoggingFilter.class);
packages("...");
}
}
And finally, a simple way to log your response inside YourLoggingFilter would be (here I assume Jackson, but this is just an example, I don't know what logger you are using, but don't open a file every time you do a request!!!)
Object obj = responseContext.getEntity();
ObjectMapper om = new ObjectMapper();
File file = new File("...");
try {
OutputStream out = new FileOutputStream(file);
om.writeValue(out, obj);
} catch (IOException e) {
// this could fail but you don't want your request to fail
e.printStackTrace();
}
Hope it helps.
Ok, So i've genetrated my java classes from my xsd file using jaxb. I've also written the following code as an endpoint which recieves a request (XML). Now I'd like to read the request into my java objects I can then use these to insert into my DB. Is this the correct way i should be implementing this? If so, how is it done? Thanks
#POST
#Consumes("application/xml")
#Produces("application/xml")
public String registerPost(#Context HttpServletRequest req) {
try {
//update DB
} catch (DatabaseException e) {
return "Fail";
}
}
Hopefully this helps a little :
Your jax-rs end point can accept your JAXB class directly (providing your server has been configured with a JAXB provider). It will handle the conversion from incoming text to appropiate Java objects for you.
You don't specifically need the Servlet Request Object.
You may need to convert your JAXB entities into JPA entities before you can save them to the database (or add both JAXB and JPA annotations to the same classes).
Your return type should be text/plain rather than XML (or you should return XML).
#POST
#Consumes("application/xml")
#Produces("text/plain")
public String registerPost(MyEntityClass payload) {
try {
//update DB
myService.save(payload);
return "success";
} catch (DatabaseException e) {
return "Fail";
}
}