Reuse tomcat threads while waiting "long" time - java

THE CONFIGURATION
Web server : Nginx
App server : Tomcat with default configuration of 200 request serving threads
Expected response time for my server : ~30 seconds(There are lots of third party dependencies)
THE SCENARIO
Every 10 seconds the application will need to generate the token for its use. The expected time for token generation is around 5 seconds, but since its a third party system being contacted over network, this is obviously not consistent and can go up to 10 seconds.
During the token generation process, nearly 80% of the incoming requests per second will need to wait.
WHAT I BELIEVE SHOULD HAPPEN
Since the requests waiting for the token generation will have to wait a "long" time, there is no reason for these request serving to be reused to serve other incoming requests while waiting for token generation process to complete.
Basically, it would make sense if my 20% to keep being served. If the waiting threads are not being utilized for other requests, tomcat request serving limit will be reached and server would essentially choke, not really something any developer will like.
WHAT I TRIED
Initially I expected switching to tomcat NIO connector would do this job. But after looking at this comparison, I was really not hopeful. Nevertheless, I tried by forcing the requests to wait for 10 second and it did not work.
Now I am thinking on the lines that I need to, sort of, shelve the request while its waiting and need to signal the tomcat that this thread is free to reuse. Similarly, I will need tomcat to give me a thread from its threadpool when the request is ready to be moved forward. But I am blindsided on how to do it or even if this is possible.
Any guidance or help?

You need an asynchronous servlet but you also need asynchronous HTTP calls to the external token generator. You will gain nothing by passing the requests from the servlet to an ExecutorService with a thread pool if you still create one thread somewhere per token request. You have to decouple threads from HTTP requests so that one thread can handle multiple HTTP requests. This can be achieved with an asynchronous HTTP client like Apache Asynch HttpClient or Async Http Client.
First you have to create an asynchronous servlet like this one
public class ProxyService extends HttpServlet {
private CloseableHttpAsyncClient httpClient;
#Override
public void init() throws ServletException {
httpClient = HttpAsyncClients.custom().
setMaxConnTotal(Integer.parseInt(getInitParameter("maxtotalconnections"))).
setMaxConnPerRoute(Integer.parseInt(getInitParameter("maxconnectionsperroute"))).
build();
httpClient.start();
}
#Override
public void destroy() {
try {
httpClient.close();
} catch (IOException e) { }
}
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
AsyncContext asyncCtx = request.startAsync(request, response);
asyncCtx.setTimeout(ExternalServiceMock.TIMEOUT_SECONDS * ExternalServiceMock.K);
ResponseListener listener = new ResponseListener();
asyncCtx.addListener(listener);
Future<String> result = httpClient.execute(HttpAsyncMethods.createGet(getInitParameter("serviceurl")), new ResponseConsumer(asyncCtx), null);
}
}
This servlet performs an asynchronous HTTP call using Apache Asynch HttpClient. Note that you may want to configure the maximum connections per route because as per RFC 2616 spec HttpAsyncClient will only allow a maximum of two concurrent connections to the same host by default. And there are plenty of other options that you can configure as shown in HttpAsyncClient configuration. HttpAsyncClient is expensive to create, therefore you do not want to create an instace of it on each GET operation.
One listener is hooked to the AsyncContext, this listener is only used in the example above to handle timeouts.
public class ResponseListener implements AsyncListener {
#Override
public void onStartAsync(AsyncEvent event) throws IOException {
}
#Override
public void onComplete(AsyncEvent event) throws IOException {
}
#Override
public void onError(AsyncEvent event) throws IOException {
event.getAsyncContext().getResponse().getWriter().print("error:");
}
#Override
public void onTimeout(AsyncEvent event) throws IOException {
event.getAsyncContext().getResponse().getWriter().print("timeout:");
}
}
Then you need a consumer for the HTTP client. This consumer informs the AsyncContext by calling complete() when buildResult() is executed internally by HttpClient as a step to return a Future<String> to the caller ProxyService servlet.
public class ResponseConsumer extends AsyncCharConsumer<String> {
private int responseCode;
private StringBuilder responseBuffer;
private AsyncContext asyncCtx;
public ResponseConsumer(AsyncContext asyncCtx) {
this.responseBuffer = new StringBuilder();
this.asyncCtx = asyncCtx;
}
#Override
protected void releaseResources() { }
#Override
protected String buildResult(final HttpContext context) {
try {
PrintWriter responseWriter = asyncCtx.getResponse().getWriter();
switch (responseCode) {
case javax.servlet.http.HttpServletResponse.SC_OK:
responseWriter.print("success:" + responseBuffer.toString());
break;
default:
responseWriter.print("error:" + responseBuffer.toString());
}
} catch (IOException e) { }
asyncCtx.complete();
return responseBuffer.toString();
}
#Override
protected void onCharReceived(CharBuffer buffer, IOControl ioc) throws IOException {
while (buffer.hasRemaining())
responseBuffer.append(buffer.get());
}
#Override
protected void onResponseReceived(HttpResponse response) throws HttpException, IOException {
responseCode = response.getStatusLine().getStatusCode();
}
}
The web.xml configuration for ProxyService servlet may be like
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0" metadata-complete="true">
<display-name>asyncservlet-demo</display-name>
<servlet>
<servlet-name>External Service Mock</servlet-name>
<servlet-class>ExternalServiceMock</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Proxy Service</servlet-name>
<servlet-class>ProxyService</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
<init-param>
<param-name>maxtotalconnections</param-name>
<param-value>200</param-value>
</init-param>
<init-param>
<param-name>maxconnectionsperroute</param-name>
<param-value>4</param-value>
</init-param>
<init-param>
<param-name>serviceurl</param-name>
<param-value>http://127.0.0.1:8080/asyncservlet/externalservicemock</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>External Service Mock</servlet-name>
<url-pattern>/externalservicemock</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Proxy Service</servlet-name>
<url-pattern>/proxyservice</url-pattern>
</servlet-mapping>
</web-app>
And a mock servlet for the token generator with a delay in seconds may be:
public class ExternalServiceMock extends HttpServlet{
public static final int TIMEOUT_SECONDS = 13;
public static final long K = 1000l;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
Random rnd = new Random();
try {
Thread.sleep(rnd.nextInt(TIMEOUT_SECONDS) * K);
} catch (InterruptedException e) { }
final byte[] token = String.format("%10d", Math.abs(rnd.nextLong())).getBytes(ISO_8859_1);
response.setContentType("text/plain");
response.setCharacterEncoding(ISO_8859_1.name());
response.setContentLength(token.length);
response.getOutputStream().write(token);
}
}
You can get a fully working example at GitHub.

This problem is essentially the reason so many "reactive" libraries and toolkits exist.
It's not a problem that can be solved by tweaking or swapping out the tomcat connector.
You basically need to remove all blocking IO calls, replacing them with non-blocking IO will likely require rewriting large parts of the application.
Your HTTP server needs to be non-blocking, you need to be using a non blocking API to the server(like servlet 3.1) and your calls to the third party API need to be non blocking.
Libraries like Vert.x and RxJava provide tooling to help with all of this.
Otherwise the only other alternative is to just increase the size of the threadpool, the operating system already takes care of scheduling the CPU so that the inactive threads don’t cause too much performance loss, but there is always going to be more overhead compared to a reactive approach.
Without knowing more about your application it is hard to offer advice on a specific approach.

Using async servlet requests or reactive libraries (as mentioned in other answers) can help but will demand major architectural changes.
Another option is to separate token updates from token uses.
Here is a naive implementation:
public class TokenHolder {
public static volatile Token token = null;
private static Timer timer = new Timer(true);
static {
// set the token 1st time
TokenHolder.token = getNewToken();
// schedule updates periodically
timer.schedule(new TimerTask(){
public void run() {
TokenHolder.token = getNewToken();
}
}, 10000, 10000);
}
}
Now your requests can just use TokenHolder.token to access the service.
In a real application you probably will use a more advanced scheduling tool.

Related

How to hook begin and end of HTTP requests in JavaEE?

tl;dr:
How do I get the ServletResponse during ServletRequestListener.requestDestroyed?
Short Version
In JavaEE, I want to know when:
when a request starts
and when a request ends
and be able to inspect the request and response objects.
Long Version
In the ASP.NET world, if you want to know when a request starts and ends, you write an IHttpModule:
public class ExampleModuleForThisQuestion : IHttpModule
{
}
And then register your "module" in the web XML configuration file:
web.config:
<system.webServer>
<modules>
<add name="DoesntMatter" type="ExampleModuleForThisQuestion "/>
</modules>
</system.webServer>
Inside your module, you can register callback handlers for:
BeginRequest event
EndRequest event
The web server infrastructure then calls you Init method. That is your opportunity to register that you want to receive notifications when a request starts, and when a request ends:
public class ExampleModuleForThisQuestion : IHttpModule
{
public void Init(HttpApplication application)
{
application.BeginRequest += new EventHandler(beginRequest); //register the "BeginRequet" event
application.EndRequest += new EventHandler(endRequest); //register the "EndRequest" event
}
}
And now we have our callbacks when a request starts:
private void beginRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
//Record the time the request started
application.Context.Items["RequestStartTime"] = DateTime.Now;
//We can even access the Request and Response objects
application.ContenxtLog(application.Context.Request.Headers["User-Agent"]);
}
And we have our callback when a request ends:
private void endRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
//We can even access the Request and Response objects
//Get the response status code (e.g. 418 I'm a teapot)
int statusCode = application.Context.Response.StatusCode;
//Get the request method (e.g. GET, POST, BREW)
String method = application.context.Request.RequestType;
//Get the path from the request (e.g. /ViewCustomer)
String path = application.context.Request.AppRelativeCurrentExecutionFilePath'
//Get when the request started - that we recorded during "Begin Request"
DateTime requestStartTime = (DateTime)application.Context.Items["RequestStartTime"];
//And we can modify the response
if ((DateTime.Now - requestStartTime).TotalSeconds = 17)
application.Context.Response.StatusCode = 451;
}
The Java Almost-Equivalent is ServletRequestListener
In Java, apparently the corresponding technique is to create and object that implements the ServletRequestListener interface:
#WebListener
public class ExampleListenerForThisQuestion
implements javax.servlet.ServletRequestListener {
}
and register our listener with the application server by including it in our web XML configuration file:
web.xml
<listener>
<listener-class>ExampleListenerForThisQuestion</listener-class>
</listener>
Now we can implement the requestInitialized and requestDestroyed methods to get when a request starts and ends:
public class ExampleListenerForThisQuestion
implements javax.servlet.ServletRequestListener {
#Override
public void requestInitialized(ServletRequestEvent sre) {
ServletRequest sr = sre.getServletRequest();
sr.setAttribute("requestStartTicks", getCurrentTickCount());
HttpServletRequest request = (HttpServletRequest) sr;
// e.g. "PUT /Customers/1234"
System.out.printf("%s %s\r\n", request.getMethod());
}
#Override
public void requestDestroyed(ServletRequestEvent sre) {
ServletRequest sr = sre.getServletRequest();
long requestStartTicks = (long)sr.getAttribute("requestStartTicks");
HttpServletResponse response = (HttpServletRequest)...nothing, because i don't know how...
// e.g. "226 IM Used"
System.out.printf("%d %s\r\n", response.getStatus(), response.getStatusDescription());
}
}
But how do we get the response?
Now that I'm notified when the response ends, I need the result of that request:
I need the HTTP status code (e.g., 424)
I need the HTTP status description (e.g., Failed Dependency)
I need to inspect response headers
I need to modify response headers
You notice the line in my code above:
HttpServletResponse response = (HttpServletRequest)...nothing, because i don't know how...
How can I get hold of the response?
You can create a Filter instead of a listener.
Filters allow you to create wrappers around request processing. See the documentation on that topic.
For HTTP, you can use HTTPFilter. This could look like the following:
#WebFilter("/*")//or via deployment descriptor
public class YourFilter extends HttpFilter{ //or just Filter for general (non-HTTP) processing
#Override
public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {//for generic filters, use ServletRequest/ServletResponse instead
//before request processing
chain.doFilter(req, res);//calls other filters and processes request
//after request processing
//you can use res here
}
}
If you do not call chain.doFilter, other filters and the servlet will not be executed.
If you prefer declaring the filter in your deployment descriptor (web.xml), you can do that as well:
<filter>
<filter-name>yourFilter</filter-name>
<filter-class>your.FilterClass</filter-class>
</filter>
<filter-mapping>
<filter-name>yourFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Conditional Http Proxy Servlet

Working on a http proxy servlet which is used to forward the request to another remote server.
NOTE: Cannot change the URL Pattern
Snippet of web.xml
<servlet>
<servlet-name>SomeServlet</servlet-name>
<servlet-class>SomeServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SomeServlet</servlet-name>
<url-pattern>/pattern1/*</url-pattern>
</servlet-mapping>
SomeServlet.Java
Validates the requests to check for specific content in request body and if matches forward to remote server. (Working as expected)
but if the condition fails, request processing should continue as is.
what I have now,
public class proxyServlet extends HttpServlet {
/* it has all the init other configs*/
#Overrirde
protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws ServletException, IOException {
/* If true, returns which endpoint for remote server else returns null */
String endPoint = checkForConditions(servletRequest);
if(endPoint == null) {
return; // this is not letting the app to continue with flow.
} else {
HttpRequest newR = new BasicHttpRequest(method, endPoint);
doExecute(servletRequest, newR)
}
}
}
I cannot create a new request and forward since server will again catch the pattern and do it in a infinite loop.
What is the best way to accomplish this? Any suggestions are greatly appreciated.
UPDATE-Solved: As per the comment from #balusc, Used servlet-filter and on condition use a HTTPClient to send POST request to remote server else chain.doFilter(...)

How to run a method after sending a servlet response while also accepting further requests?

The scenario I am trying to complete is the following:
The client submits a HTTP POST request to the servlet.
The servlet sends a response to the client confirming the request has been received.
The servlet then sends an email notification to the system administrator.
So far I am able to complete the following steps in the order described above, however I run into an issue at the end. If the client makes another HTTP request to the same or another servlet while the email notification method EmailUtility.sendNotificationEmail() is still running, the servlet will not run any further code until this email method is completed (I am using javax.mail to send emails if that matters).
I have tried to use AsyncContext to get around this (which I may be using incorrectly), but unfortunately the issue still remains.
How can I make the EmailUtility.sendNotificationEmail() run in a different thread/asynchronously so that the servlets do not have to wait for this method to complete?
This is my code so far:
//Step 1: Client submits POST request to servlet.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
//Step 2: Servlet sends response to client.
response.getWriter().write("Your request has been received");
response.getOutputStream().flush();
response.getOutputStream().close();
//Step 3: Servlet send email notification.
final AsyncContext acontext = request.startAsync();
acontext.start(new Runnable() {
public void run() {
EmailUtility.sendNotificationEmail();
acontext.complete();
}
});
}
Try something simple, like a thread:
new Thread(new Runnable()
{
#Override
public void run()
{
EmailUtility.sendNotificationEmail();
}
}, "Send E-mail").start();
So I resolved the issue by using ExecutorService as follows:
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
public void run() {
EmailUtility.sendNotificationEmail();
}
});
executorService.shutdown();

How to properly report an error to client through WebSockets

How do I properly close a websocket and and provide a clean, informative response to the client when an internal error occurs on my server? In my current case, the client must provide a parameter when it connects, and I am trying to handle incorrect or missing parameters received by OnOpen.
This example suggests I can just throw an exception in OnOpen, which will ultimately call OnError where I can close with a reason and message. It kinda works, but the client only receives an EOF, 1006, CLOSE_ABNORMAL.
Also, because I have found no other discussion, I can't tell what might be best practice.
I'm using the JSR-356 spec, as follows:
#ClientEndpoint
#ServerEndpoint(value="/ws/events/")
public class WebSocketEvents
{
private javax.websocket.Session session;
private long token;
#OnOpen
public void onWebSocketConnect(javax.websocket.Session session) throws BadRequestException
{
logger.info("WebSocket connection attempt: " + session);
this.session = session;
// this throws BadRequestException if null or invalid long
// with short detail message, e.g., "Missing parameter: token"
token = HTTP.getRequiredLongParameter(session, "token");
}
#OnMessage
public void onWebSocketText(String message)
{
logger.info("Received text message: " + message);
}
#OnClose
public void onWebSocketClose(CloseReason reason)
{
logger.info("WebSocket Closed: " + reason);
}
#OnError
public void onWebSocketError(Throwable t)
{
logger.info("WebSocket Error: ");
logger.debug(t, t);
if (!session.isOpen())
{
logger.info("Throwable in closed websocket:" + t, t);
return;
}
CloseCode reason = t instanceof BadRequestException ? CloseReason.CloseCodes.PROTOCOL_ERROR : CloseReason.CloseCodes.UNEXPECTED_CONDITION;
try
{
session.close(new CloseReason(reason, t.getMessage()));
}
catch (IOException e)
{
logger.warn(e, e);
}
}
}
Edit: The exception throwing per linked example seems weird, so now I am catching exception within OnOpen and immediately doing
session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "some text"));
Edit: This turned out to be correct, though a separate bug disguised it for a while.
Edit2: Clarification: HTTP is my own static utility class. HTTP.getRequiredLongParameter() gets query parameters from the client's initial request by using
session.getRequestParameterMap().get(name)
and does further processing.
To develop the points I mentioned, your problem about "how to handle a required parameter", I can see following options. First of all, let's consider the endpoint:
#ServerEndpoint(value = "/websocket/myendpoint",
configuration = MyWebsocketConfiguration.class)
public class MyEndpoint{
// #OnOpen, #OnClose, #OnMessage, #OnError...
}
Filtering
The first contact between a client and a server is a HTTP request. You can filter it with a filter to prevent the websocket handshake from happening. A filter can either block a request or let it through:
import javax.servlet.Filter;
public class MyEndpointFilter implements Filter{
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// nothing for this example
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// if the connection URL is /websocket/myendpoint?parameter=value
// feel free to dig in what you can get from ServletRequest
String myToken = request.getParameter("token");
// if the parameter is mandatory
if (myToken == null){
// you can return an HTTP error code like:
((HttpServletResponse) response).setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
// if the parameter must match an expected value
if (!isValid(myToken)){
// process the error like above, you can
// use the 403 HTTP status code for instance
return;
}
// this part is very important: the filter allows
// the request to keep going: all green and good to go!
chain.doFilter(request, response);
}
#Override
public void destroy() {
//nothing for this example
}
private boolean isValid(String token){
// how your token is checked? put it here
}
}
If you are using a filter, you must add it in your web.xml:
<web-app ...>
<!-- you declare the filter here -->
<filter>
<filter-name>myWebsocketFilter</filter-name>
<filter-class>com.mypackage.MyEndpointFilter </filter-class>
<async-supported>true</async-supported>
</filter>
<!-- then you map your filter to an url pattern. In websocket
case, it must match the serverendpoint value -->
<filter-mapping>
<filter-name>myWebsocketFilter</filter-name>
<url-pattern>/websocket/myendpoint</url-pattern>
</filter-mapping>
</web-app>
the async-supported was suggested by BalusC in my question to support asynchronous message sending.
TL,DR
if you need to manipulate GET parameters provided by the client at the connection time, Filter can be a solution if you are satisfied with a pure HTTP answer (403 status code and so on)
Configurator
As you may have noticed, I have added configuration = MyWebsocketConfiguration.class. Such class looks like:
public class MyWebsocketConfigurationextends ServerEndpointConfig.Configurator {
// as the name suggests, we operate here at the handshake level
// so we can start talking in websocket vocabulary
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
// much like ServletRequest, the HandshakeRequest contains
// all the information provided by the client at connection time
// a common usage is:
Map<String, List<String>> parameters = request.getParameterMap();
// this is not a Map<String, String> to handle situation like
// URL = /websocket/myendpoint?token=value1&token=value2
// then the key "token" is bound to the list {"value1", "value2"}
sec.getUserProperties().put("myFetchedToken", parameters.get("token"));
}
}
Okay, great, how is this different from a filter? The big difference is that you're adding here some information in the user properties during the handshake. That means that the #OnOpen can have access to this information:
#ServerEndpoint(value = "/websocket/myendpoint",
configuration = MyWebsocketConfiguration.class)
public class MyEndpoint{
// you can fetch the information added during the
// handshake via the EndpointConfig
#OnOpen
public void onOpen(Session session, EndpointConfig config){
List<String> token = (List<String>) config.getUserProperties().get("myFetchedToken");
// now you can manipulate the token:
if(token.isEmpty()){
// for example:
session.close(new CloseReasons(CloseReason.CloseCodes.CANNOT_ACCEPT, "the token is mandatory!");
}
}
// #OnClose, #OnMessage, #OnError...
}
TL;DR
You want to manipulate some parameters but process the possible error in a websocket way? Create your own configuration.
Try/catch
I also mentioned the try/catch option:
#ServerEndpoint(value = "/websocket/myendpoint")
public class MyEndpoint{
#OnOpen
public void onOpen(Session session, EndpointConfig config){
// by catching the exception and handling yourself
// here, the #OnError will never be called.
try{
Long token = HTTP.getRequiredLongParameter(session, "token");
// process your token
}
catch(BadRequestException e){
// as you suggested:
session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "some text"));
}
}
// #OnClose, #OnMessage, #OnError...
}
Hope this help
I believe I should have placed...
session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "some text"));
...right where the error occurs, within #OnOpen(). (For generic errors, use CloseCodes.UNEXPECTED_CONDITION.)
The client receives:
onClose(1003, some text)
This is, of course, the obvious answer. I think I was misled, by the example cited, into throwing an exception from #OnOpen(). As Remy Lebeau suggested, the socket was probably closed by this, blocking any further handling by me in #OnError(). (Some other bug may have obscured the evidence that was discussed.)

Sending an xml response back when a url is sent

my task is to check if my program is running fine on server or not.The scenario is like this :A URL is sent to the program that is continuously running and it is supposed to reply with a status OK if it is running fine.I am not able to understand the process how to do it.Can anyone explain.
OK, I am assuming that you wanted to write some sort of health check page for your application. So here it goes.
package test.naishe.so;
public class HealthCheck extends HttpServlet {
private static final long serialVersionUID = 940861886429788526L;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
int response = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
boolean checkServices = true;
//optional: assumming you wanted to do health check on various services (eg. DB service) and have a utility class for that
checkServices = CheckServices.checkAll();
if(checkServices)
response = HttpServletResponse.SC_OK;
String out =
"<healthCheck>" +
"<services>" +
(checkServices?"OK":"ERROR")
"</services>" +
"</healthCheck>"
;
resp.setStatus(response);
resp.getWriter().println(out);
}
}
in your web.xml add the following:
<servlet>
<servlet-name>healthCheck</servlet-name>
<servlet-class>test.naishe.so.HealthCheck</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>healthCheck</servlet-name>
<url-pattern>/check_health</url-pattern>
</servlet-mapping>
you can access this page from <base_url>/check_health on your local machine, http://localhost[:port][/app_name]/check_health

Categories

Resources