I am writing spring rest service using spring boot framework and jetty as container.
In controller I have used the callable like this.
#RequestMapping(value = "/{key}/events", method = RequestMethod.GET)
public Callable<String> getEvents(#PathVariable("key") final String key,
#RequestParam(required = false) final String startAt,
#RequestParam(required = false) final String maxResults) {
return new Callable<String>() {
#Override
public String call() throws Exception {
// here logic that return json string
}
}
}
and the servlet conatiner factory I wrote like this
#Bean
public EmbeddedServletContainerFactory servletContainer(){
JettyEmbeddedServletContainerFactory jetty=new JettyEmbeddedServletContainerFactory();
jetty.addServerCustomizers(new JettyServerCustomizer() {
#Override
public void customize(final Server server) {
// Tweak the connection pool used by Jetty to handle incoming HTTP connections
final QueuedThreadPool threadPool = server.getBean(QueuedThreadPool.class);
threadPool.setMaxThreads(Integer.valueOf(200));
threadPool.setMinThreads(Integer.valueOf(100));
threadPool.setIdleTimeout(Integer.valueOf(100000));
threadPool.setStopTimeout(10000);
}
});
jetty.setPort(4040);
jetty.setContextPath("/mycontextpath");
return jetty;
}
Now My problem is when I run application,
I hit the url from browser first time it not gives the output (service not available message from jetty server).
but again I hit the URL second, third, fourth time it gives me the output.
So for implementing Callable as Controller do I missing something?
I debug code and found that first time internal processing is still going on and the browser complete response...
so What I do? please suggest,
My main aim is that controller should accept more client request at particular time and respond.
Related
Context
I am currently working on a JavaEE project with a lot of existing resource based JAX-RS services. For this project we would like to have batch processing to prevent a lot of separate calls and, most importantly, to execute these different methods in a transactional context for rollback purposes with the native MongoDB driver. We want to avoid manually creating new methods for all possible combinations. I could not find any solution to this issue on Stack Overflow so I started analyzing the implementation of RESTEasy and I came up with the following solution.
Below a simplified/pseudo version of my code:
JAX-RS method
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("execute")
public Response executeBatch(BatchRequestWrapper batchRequestWrapper) throws UnsupportedEncodingException
{
// Retrieve information from context
HttpServletRequest httpServletRequest = ResteasyProviderFactory.getContextData(HttpServletRequest.class);
HttpServletResponse httpServletResponse = ResteasyProviderFactory.getContextData(HttpServletResponse.class);
ServletContext servletContext = ResteasyProviderFactory.getContextData(ServletContext.class);
HttpResponse httpResponse = ResteasyProviderFactory.getContextData(HttpResponse.class);
SynchronousDispatcher dispatcher = (SynchronousDispatcher) ResteasyProviderFactory.getContextData(Dispatcher.class);
ResteasyHttpHeaders httpHeaders = (ResteasyHttpHeaders) ResteasyProviderFactory.getContextData(HttpHeaders.class);
ResteasyUriInfo uriInfo = (ResteasyUriInfo) ResteasyProviderFactory.getContextData(UriInfo.class);
// Create Mongo Client Session object and save it in a Singleton which contains a ThreadLocal object so that DAO layer can reuse the client session object for all methods.
// Iterate over all the methods and invoke dispatcher
for (BatchRequest batchRequest : batchRequestWrapper.getBatchRequests())
{
// Update URI based on specific endpoint
uriInfo.setRequestUri(URI.create(batchRequest.getUri()));
// Temporary use mock response for the response
MockHttpResponse response = new MockHttpResponse();
// Create httpservletinput message from RESTEasy lib to pass to the dispatcher. It will automatically resolve all parameters/methods etc.
HttpServletInputMessage request = new HttpServletInputMessage(httpServletRequest, httpServletResponse, servletContext, httpResponse, httpHeaders, uriInfo, batchRequest.getHttpMethod(), dispatcher);
// Set body in input stream if body is specified. This will inject the correct 'body' parameters in the methods. Query and Path parameters are already resolved in the method above.
if(!Strings.isNullOrEmpty(batchRequest.getBody()))
{
InputStream targetStream = new ByteArrayInputStream(batchRequest.getBody().getBytes(StandardCharsets.UTF_8));
request.setInputStream(targetStream);
}
// Actual invoke
dispatcher.invoke(request, response);
// Do something with response object
}
// Clean or abort session based on invoke result
return Response.ok().entity(null).build();
}
Request Object
public class BatchRequestWrapper
{
private List<BatchRequest> batchRequests;
public List<BatchRequest> getBatchRequests()
{
return batchRequests;
}
public void setBatchRequests(List<BatchRequest> batchRequests)
{
this.batchRequests = batchRequests;
}
}
public class BatchRequest
{
private String uri;
private String httpMethod;
private String body;
public String getUri()
{
return uri;
}
public void setUri(String uri)
{
this.uri = uri;
}
public String getHttpMethod()
{
return httpMethod;
}
public void setHttpMethod(String httpMethod)
{
this.httpMethod = httpMethod;
}
public String getBody()
{
return body;
}
public void setBody(String body)
{
this.body = body;
}
}
My solution works with one new REST method and let's me reuse all the existing JAX-RS annotated methods in the project. Before I actually fully implement this and bring it to production, I would like to know if this is the way to actually do this or are there better alternatives? I am not a big fan of the hard dependency on RESTEasy though.
I have a GET method inside a controller that get a lot of parameters. I want to redirect the URL in case of exception or invalid input and return (print to the client) the exception with addition message.
My function looks like this:
#GetMapping("/v1.0/hello/{userName}")
public ClientResponse getDetails(#PathVariable(value = "userName") String userName,
#RequestParam(value = "expInp1", required = false) int expInp1,
#RequestParam(value = "expInp2", required = false) int expInp2,
#RequestParam(value = "expInp3", required = false) int expInp3){
// do something...
return clientResponse;
}
ClientResponse is an object that contain all the relevant details and can't be changed.
For example, if someone inserts the following URL /v1.0/hello/{userName}?expInp4=XXX, I want to redirect them to /v1.0/hello/ with a suitable message.
Is there a Spring annotation doing that without a lot of code? And if not, what is the best way in my case?
You can take a look at #RestControllerAdvice coupled with #ExceptionHandler annotation.
you can follow these steps to create a redirect system:
create your own exception
public class RedirectException extends RuntimeException{
private String redirectUrl;
public RedirectException(String message, String rUrl) {
super(message);
this.redirectUrl = rUrl;
}
}
create your controller advice
#RestControllerAdvice
public class ErrorController {
#ExceptionHandler(RedirectExecption.class)
public void handleRedirect(RedirectException re, HttpServletResponse res) {
res.sendRedirect(re.getRedirectUrl);
}
}
when you want to send a redirect response in you running code just throw a redirectException with the redirecturl you want to reach
p.s. you can use the same mechanism for building an error handling system.
create a new class with #RestControllerAdvice or #ControllerAdvice annotation to handle the exception globally in Spring Boot.
Refer this link
I have some REST APIs that may take a while to execute, and I want to limit their execution duration. Preferably, if 30 seconds passed and the request didn't return, I would like to return a specific HTTP code / data and terminate that request completly.
The current code:
#RestController
#CrossOrigin(origins = {"*"}, maxAge = 4800, allowCredentials = "false")
public class APIController {
#RequestMapping(value = "/api/myapifunc", method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<?> optimize(#RequestParam(value="param1", defaultValue="")) {
// Code here
}
It looks like you are describing the Circuit Breaker pattern. If you have control over both the client and server code and want to explore Spring Cloud and Netflix Hysterix libraries you can take a look at Getting Started: Circuit Breaker guide.
If you are using Apache Tomcat as your servlet container you can configure Stuck Thread Detection Valve:
This valve allows to detect requests that take a long time to process, which might indicate that the thread that is processing it is stuck. Additionally it can optionally interrupt such threads to try and unblock them.
When such a request is detected, the current stack trace of its thread is written to Tomcat log with a WARN level.
The IDs and names of the stuck threads are available through JMX in the stuckThreadIds and stuckThreadNames attributes. The IDs can be used with the standard Threading JVM MBean (java.lang:type=Threading) to retrieve other information about each stuck thread.
With Spring Boot 2.3 / Tomcat 9, you can set a timeout for ALL incoming HTTP requests to complete by installing a Tomcat StuckThreadDetectionValve. Here's the Spring configuration code you'll need (it's Kotlin):
import org.apache.catalina.valves.StuckThreadDetectionValve
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
#Configuration
class RequestTimeoutConfiguration(
#Value("\${app.tomcat.stuck-thread-detection.request-timeout.seconds}")
private val stuckThreadTimeoutSeconds: Int
) {
#Bean
fun stuckThreadDetectionValve() =
StuckThreadDetectionValve().apply {
threshold = stuckThreadTimeoutSeconds
interruptThreadThreshold = stuckThreadTimeoutSeconds
}
#Bean
fun stuckThreadDetectionWebServerFactoryCustomizer(valve: StuckThreadDetectionValve) =
WebServerFactoryCustomizer { factory: TomcatServletWebServerFactory ->
factory.addContextValves(valve)
}
}
Then you just need the property in application.properties to control it:
app.tomcat.stuck-thread-detection.request-timeout.seconds=130
#RequestMapping(value = "/api/myapifunc", method = RequestMethod.POST, produces =
"application/json")
public ResponseEntity<?> optimize(#RequestParam(value="param1", defaultValue="")) {
return new Callable<String>() {
#Override
public String call() throws Exception {
Thread.sleep(3000); //this will cause a timeout
return "foobar";
}
};
}
Future you can use or annotation
#Timed
#Transactional(timeout = 3000)
You can use Future timeout:
final Future<Object> submit = service.submit(new Callable<Object>() {
#Override
public Object call() throws Exception {
......YOUR CODE
return "";
}
});
try {
submit.get(3, TimeUnit.SECONDS);
} catch (Exception e) {
log.error("fail",e);
}
You can set this property configuration
server.connection-timeout=30000
in your application.properties.
Based on official documentation says:
server.connection-timeout= # Time that connectors wait for another HTTP request before closing the connection. When not set, the connector's container-specific default is used. Use a value of -1 to indicate no (that is, an infinite) timeout.
Using the latest Spring Cloud and Spring Boot, I've got a micro services layout with a Zuul gateway. At the moment when a user sends a get request their JWT token gets added to the request and that goes off to the microservice where they're authenticated and things go as usual. This all works perfectly.
Where I'm a little stuck is when handling POST/PATCH/DELETE requests. These don't go directly to the microservice they're destined for, but instead go into a messaging queue. The queue contains a simple POJO with a task and information about the task to perform, along with the users JWT.
When the receiving microservice picks up the message from the queue and starts processing it, the user isn't technically logged into the microservice like they are with a GET request. This makes it hard to do things that require knowing who the user is. Sure each time I need to know who the person is I can look them up, but that seems clunky.
I've thought about creating a REST controller for the POST/PATCH/DELETE commands and having the queue listener just call itself from these, adding the token from the task. This would effectively be the same as a GET request as far as Spring security would care.
Is this the proper pattern? Or is there a simple programmatically way to log a user in with a JWT? I've seen a few examples using Username/Passwords but not sure how to transcribe that to using a JWT.
Thank you Andy Brown for the confirmation I wasn't completely nuts doing it this way. For anyone interested it's a very simple solution that looks like this:
The queue service is just listening for events (In this case from AWS SQS) and as soon as an event comes through it gets fired off to a command controller for processing.
#Service
#EnableSqs
public class QueueListener {
private final static String SERVICE = "http://instance-service/instances";
#Autowired
private JsonTransformService jsonTransformService;
#Autowired
private RestTemplate restTemplate;
#MessageMapping("${queues.instanceEvents}")
public void instanceCommandHandler(String payload) {
// Transform the payload to the object so we can get the preset JWT
Instance instance = jsonTransformService.read(Instance.class, payload);
// Load the JWT into the internal request header, without this a 403 is thrown
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + instance.getUserToken());
HttpEntity<String> instanceEntity = new HttpEntity<>(payload, headers);
// Decide and set where to fire the request to
String endpoint;
switch (instance.getSwordfishCommand()) {
case "start": {
endpoint = "/start";
break;
}
case "stop": {
endpoint = "/stop";
break;
}
case "create": {
endpoint = "/create";
break;
}
case "reboot": {
endpoint = "/reboot";
break;
}
case "terminate": {
endpoint = "/terminate";
break;
}
default: {
endpoint = "/error";
}
}
// Fire the initial string payload through to the correct controller endpoint
restTemplate.exchange(SERVICE + endpoint, HttpMethod.POST, instanceEntity, String.class);
}
}
And a very simple REST controller which actions the tasks
#RestController
#RequestMapping("/instances")
public class InstanceCommandController {
#Autowired
private EC2Create ec2Create;
#Autowired
private EC2Start ec2Start;
#Autowired
private EC2Stop ec2Stop;
#Autowired
private EC2Reboot ec2Reboot;
#Autowired
private EC2Terminate ec2Terminate;
#Autowired
private JsonTransformService jsonTransformService;
#PostMapping("/create")
public void create(#RequestBody String payload) {
ec2Create.process(jsonTransformService.read(Instance.class, payload));
}
#PostMapping("/start")
public void start(#RequestBody String payload) {
ec2Start.process(jsonTransformService.read(Instance.class, payload));
}
#PostMapping("/stop")
public void stop(#RequestBody String payload) {
ec2Stop.process(jsonTransformService.read(Instance.class, payload));
}
#PostMapping("/reboot")
public void reboot(#RequestBody String payload) {
ec2Reboot.process(jsonTransformService.read(Instance.class, payload));
}
#PostMapping("/terminate")
public void terminate(#RequestBody String payload) {
ec2Terminate.process(jsonTransformService.read(Instance.class, payload));
}
}
This follows the CQRS pattern very well while still authenticating the user on each call. For me this is very useful as I have a AmazonEC2Async client which makes use of the users own access and secret token in each request.
Cheers for the help!
Note: see update at the bottom of the question for what I eventually concluded.
I need to send multiple responses to a request over the web socket that sent the request message, the first one quickly, and the others after the data is verified (somewhere between 10 and 60 seconds later, from multiple parallel threads).
I am having trouble getting the later responses to stop broadcasting over all open web sockets. How do I get them to only send to the initial web socket? Or should I use something besides Spring STOMP (because, to be honest, all I want is the message routing to various functions, I don't need or want the ability to broadcast to other web sockets, so I suspect I could write the message distributor myself, even though it is reinventing the wheel).
I am not using Spring Authentication (this is being retrofitted into legacy code).
On the initial return message, I can use #SendToUser, and even though we don't have a user, Spring only sends the return value to the websocket that sent the message. (see this question).
With the slower responses, though, I think I need to use SimpMessagingTemplate.convertAndSendToUser(user, destination, message), but I can't, because I have to pass in the user, and I can't figure out what user the #SendToUser used. I tried to follow the steps in this question, but didn't get it to work when not authenticated (principal.getName() returns null in this case).
I've simplified this considerably for the prototype, so don't worry about synchronizing threads or anything. I just want the web sockets to work correctly.
Here is my controller:
#Controller
public class TestSocketController
{
private SimpMessagingTemplate template;
#Autowired
public TestSocketController(SimpMessagingTemplate template)
{
this.template = template;
}
// This doesn't work because I need to pass something for the first parameter.
// If I just use convertAndSend, it broacasts the response to all browsers
void setResults(String ret)
{
template.convertAndSendToUser("", "/user/topic/testwsresponse", ret);
}
// this only sends "Testing Return" to the browser tab hooked to this websocket
#MessageMapping(value="/testws")
#SendToUser("/topic/testwsresponse")
public String handleTestWS(String msg) throws InterruptedException
{
(new Thread(new Later(this))).start();
return "Testing Return";
}
public class Later implements Runnable
{
TestSocketController Controller;
public Later(TestSocketController controller)
{
Controller = controller;
}
public void run()
{
try
{
java.lang.Thread.sleep(2000);
Controller.setResults("Testing Later Return");
}
catch (Exception e)
{
}
}
}
}
For the record, here is the browser side:
var client = null;
function sendMessage()
{
client.send('/app/testws', {}, 'Test');
}
// hooked to a button
function test()
{
if (client != null)
{
sendMessage();
return;
}
var socket = new SockJS('/application-name/sendws/');
client = Stomp.over(socket);
client.connect({}, function(frame)
{
client.subscribe('/user/topic/testwsresponse', function(message)
{
alert(message);
});
sendMessage();
});
});
And here is the config:
#Configuration
#EnableWebSocketMessageBroker
public class TestSocketConfig extends AbstractWebSocketMessageBrokerConfigurer
{
#Override
public void configureMessageBroker(MessageBrokerRegistry config)
{
config.setApplicationDestinationPrefixes("/app");
config.enableSimpleBroker("/queue", "/topic");
config.setUserDestinationPrefix("/user");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry)
{
registry.addEndpoint("/sendws").withSockJS();
}
}
UPDATE: Due to the security issues involved with the possibility of information being sent over other websockets than the originating socket, I ended up recommending to my group that we do not use the Spring 4.0 implementation of STOMP over Web Sockets. I understand why the Spring team did it the way they did it, and it is more power then we needed, but the security restrictions on our project were severe enough, and the actual requirements were simple enough, that we decided to go a different way. That doesn't invalidate the answers below, so make your own decision based on your projects needs. At least we have hopefully all learned the limitations of the technology, for good or bad.
Why don't you use a separate topic for each client?
Client generates a session id.
var sessionId = Math.random().toString(36).substring(7);
Client subscribes to /topic/testwsresponse/{sessionId}, then sends a message to '/app/testws/{sessionId}'.
In your controller you use #MessageMapping(value="/testws/{sessionId}") and remove #SendToUser. You can use #DestinationVariable to access sessionId in your method.
The controller sends further responses to /topic/testwsresponse/{sessionId}.
Essentially Spring does a similar thing internally when you use user destinations. Since you don't use Spring Authentication you cannot rely on this mechanism but you can easily implement your own as I described above.
var client = null;
var sessionId = Math.random().toString(36).substring(7);
function sendMessage()
{
client.send('/app/testws/' + sessionId, {}, 'Test');
}
// hooked to a button
function test()
{
if (client != null)
{
sendMessage();
return;
}
var socket = new SockJS('/application-name/sendws/');
client = Stomp.over(socket);
client.connect({}, function(frame)
{
client.subscribe('/topic/testwsresponse/' + sessionId, function(message)
{
alert(message);
});
// Need to wait until subscription is complete
setTimeout(sendMessage, 1000);
});
});
Controller:
#Controller
public class TestSocketController
{
private SimpMessagingTemplate template;
#Autowired
public TestSocketController(SimpMessagingTemplate template)
{
this.template = template;
}
void setResults(String ret, String sessionId)
{
template.convertAndSend("/topic/testwsresponse/" + sessionId, ret);
}
#MessageMapping(value="/testws/{sessionId}")
public void handleTestWS(#DestinationVariable String sessionId, #Payload String msg) throws InterruptedException
{
(new Thread(new Later(this, sessionId))).start();
setResults("Testing Return", sessionId);
}
public class Later implements Runnable
{
TestSocketController Controller;
String sessionId;
public Later(TestSocketController controller, String sessionId)
{
Controller = controller;
this.sessionId = sessionId;
}
public void run()
{
try
{
java.lang.Thread.sleep(2000);
Controller.setResults("Testing Later Return", sessionId);
}
catch (Exception e)
{
}
}
}
}
Just tested it, works as expected.
This is not full answer. Just general consideration and suggestion.
You cannot do different stuff or type of connection via the same socket. Why not have different sockets for different work? Some with authentication and some without. Some for quick task and some for long execution.