I am using Tomcat 9.0 to run Java code on some data from a database.
I have a single servlet that is invoked by directly accessing x.x.x.x:8080/myapp/myservlet.
The length of time to complete is between 5 seconds - 1 minute.
The servlet returns a response right away, leaving it to continue processing in the background. I am not sure Tomcat is supposed to be used like this.
The problem is until the processing has actually finished, the web client cannot access x.x.x.x:8080/myapp/myservlet.
Each new web client can connect and invoke the servlet fine.
I simply want to invoke my java code as a background process in a fire and forget manner. Is this possible with Tomcat?
Any guidance would be great
Move you code in a Thread or an executor and use the servlet only to start and monitor the execution.
public class myservlet extends HttpServlet
{
...
Thread t = null;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/plain;charset=UTF-8");
PrintWriter out = response.getWriter();
if(t == null || !t.isAlive())
{
t = new Thread(new Runner());
t.setDaemon(true);
t.start();
out.write("Process started.\n");
}
else
out.write("Process running...\n");
}
public static class Runner implements Runnable
{
public void run()
{
// put your code here
}
}
}
Related
I am using spring for out web application. One API does a lot of computation which looks like this.
void serviceMethod(){
fetchFromDB();
veryLongComputation1(); //1
veryLongComputation2(); //2
veryLongComputation3(); //3
}
My API takes a lot of time to run. Steps 1,2 and 3 takes lot of time because they have lot of computation and lot of IO (to db) too.
What I want is to return the response and run the 1,2,3 in a thread. But problem with that approach is if my application crashes, this code will never be executed.
Can someone suggest some approaches to encounter this problem? One thing to remember, there will be many instances of the application.
Java provides an async servlet to handle requests which takes long time to complete. The basic idea is Http thread in the servlet container triggers the computation and returns immediately whereas the response is sent only when the computation is complete. See the sample below
#WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {
/* ... Same variables and init method as in SyncServlet ... */
#Override
public void doGet(HttpServletRequest request,
HttpServletResponse response) {
response.setContentType("text/html;charset=UTF-8");
final AsyncContext acontext = request.startAsync();
acontext.start(new Runnable() {
public void run() {
String param = acontext.getRequest().getParameter("param");
String result = resource.process(param);
HttpServletResponse response = acontext.getResponse();
/* ... print to the response ... */
acontext.complete();
}
}
The same thing in spring
#GetMapping(value = "/asyncNonBlockingRequestProcessing")
public CompletableFuture<String> asyncNonBlockingRequestProcessing(){
ListenableFuture<String> listenableFuture = getRequest.execute(new AsyncCompletionHandler<String>() {
#Override
public String onCompleted(Response response) throws Exception {
logger.debug("Async Non Blocking Request processing completed");
return "Async Non blocking...";
}
});
return listenableFuture.toCompletableFuture();
}
i'm trying Schedule a job with Quartz and Cant Schedule ErpConfigContext , when i do a request, works fine.
But in scheduled job, this return a error.
//Request working
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
new ODataScheduledFetch().execute();
}
//Job class not working
public class JobProductPricing implements Job {
#Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
// TODO Auto-generated method stub
new ODataScheduledFetch().execute();
}
}
ODataScheduledFetch().execute(), do some thinks and call this method,
and when execute query.execute(new ErpConfigContext()); return a error only on Schedule Job
private boolean tryRequestERP(ODataQuery query,ODataQueryResult[] queryResult) {
boolean boReturn=false;
try {
//queryResult
queryResult[0] = query.execute(new ErpConfigContext());
boReturn = true;
}catch(Exception e) {
String error = e.toString();
System.out.println(error);
boReturn = false;
}
return boReturn;
}
And received this error:
[
com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException: Failed to get ConnectivityConfiguration:
no RequestContext available. Have you correctly configured a RequestContextServletFilter or have you wrapped your logic in a
RequestContextExecutor when executing background tasks that are not triggered by a request?
]
on this Creating ErpConfigContext threw exception
has a answer that i dont get...
"
EDIT: Note that when running your code in a background job (not triggered by a request), you have to wrap your code with RequestContextExecutor::execute.
"
Looking at your code sample I assume you are not using Spring Boot, which is the focus of the linked question.
What you need to do is wrapping your scheduled call into a RequestContextExecutor::execute call. For your example it would look like this:
#Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
new RequestContextExecutor().execute(() -> {
new ODataScheduledFetch().execute();
});
}
Depending on what you do inside your ODataScheduledFetch::execute call you might need to use the JwtBasedRequestContextExecutor (on CloudFoundry only).
For a bit mor background:
All *Accessor classes (e.g. TenantAccessor, DestinationAccessor) rely on a so called RequestContext to be available in the current Thread/Request. Such a RequestContext is created by the RequestContextExecutor as described above.
To make your life easier in the usual use-case (which is on receiving Requests) we automatically load the RequestContextServletFilter which wraps all incoming Requests for you.
That is the reason why your code works in a Request but not scheduled.
I just came to a scenario where this question popped on my head and I cannot find an answer, please suggest
We call the callback methods in a thread by any approach, my approach would be:
interface Callback {
void callback();
}
class MyThread implements Runnable {
Callback cb;
public MyThread(Callback cb) {
this.bc = cb;
}
public void run() {
// my task to do
this.cb.callback();
}
}
Example of a request made to a servlet on asynchronous mode,
Reference: https://docs.oracle.com/javaee/7/tutorial/doc/servlets012.htm
#WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {
#Override
public void doGet(HttpServletRequest request,
HttpServletResponse response) {
response.setContentType("text/html;charset=UTF-8");
final AsyncContext acontext = request.startAsync();
acontext.start(new Runnable() {
public void run() {
String param = acontext.getRequest().getParameter("param");
String result = resource.process(param);
HttpServletResponse response = acontext.getResponse();
acontext.complete();
}
}
AsyncServlet adds asyncSupported=true to the #WebServlet annotation.
The rest of the differences are inside the service method.
request.startAsync() causes the request to be processed
asynchronously; the response is not sent to the client at the end of
the service method.
acontext.start(new Runnable() {...}) gets a new thread from the
container.
Question - Is it necessary that all callbacks will be made in multiple threads, when requests are made on asynchronous mode?
My answer is like .. It need not be, it basically depends on where the callback methods are defined, in one thread or multiple threads.
Please suggest
Here are the requirements for the problem:
java web application running in Tomcat 7
The initialization code needs to talk to an external database
The external database might not be available during start-up of the application
The application start-up can not fail otherwise tomcat will mark the application as not running and will not send the application any requests. Instead the application should start-up accept requests and if it finds that the app specific initialization is not complete it should try to complete it during the request processing.
I am trying to use the servlet filter below to solve the problem. and I have following questions.
Is this Servlet filter thread safe, will it execute the initialization code only once?
Is there a way to solve this problem without using a servlet filter?
Is there a better way to solve this problem?
package com.exmaple;
#WebFilter("/*")
public class InitFilter implements Filter
{
private volatile boolean initialized = false;
public void destroy() {
System.out.println("InitFilter.destroy()");
}
// is this method thread safe and will only execute the init code once
// and will cause all requests to wait until initialization code is executed
// thread code
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("InitFilter.doFilter()");
if (initialized == false) {
synchronized (this) {
// do expensive initialization work here
initialized = true;
}
}
chain.doFilter(request, response);
}
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("InitFilter.init()");
}
}
I would approach it as a ServletContextListener and run the initialization in the contextInitialized method, as a separate thread, maybe using a FutureTask as #fge suggests, or a newSingleThreadExecutor rather than a pool.
Maybe something along the lines of...
public void contextInitialized(ServletContextEvent sce)
{
Executors.newSingleThreadExecutor().execute(new Runnable() {
#Override
public void run()
{
// Long running initialization code, or a call to it.
}
});
}
This avoids problems with synchronization and only runs the initialization once. (Once per Context)
In any case, doing this or a Filter, the rest of your program has to deal with initialization being incomplete.
I recommend you to put your long-running initialization in a thread, like:
public void init() throws ServletException {
//Configure logging, app, pool ...
MyAppStarter.getInstance().start();
}
Hey I want to implement a Java Servlet that starts a thread only once for every single user. Even on refresh it should not start again. My last approach brought me some trouble so no code^^. Any Suggestions for the layout of the servlet?
public class LoaderServlet extends HttpServlet {
// The thread to load the needed information
private LoaderThread loader;
// The last.fm account
private String lfmaccount;
public LoaderServlet() {
super();
lfmaccount = "";
}
#Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
if (loader != null) {
response.setContentType("text/plain");
response.setHeader("Cache-Control", "no-cache");
PrintWriter out = response.getWriter();
out.write(loader.getStatus());
out.flush();
out.close();
} else {
loader = new LoaderThread(lfmaccount);
loader.start();
request.getRequestDispatcher("WEB-INF/pages/loader.jsp").forward(
request, response);
}
}
#Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
if (lfmaccount.isEmpty()) {
lfmaccount = request.getSession().getAttribute("lfmUser")
.toString();
}
request.getRequestDispatcher("WEB-INF/pages/loader.jsp").forward(
request, response);
}
}
The jsp uses ajax to regularly post to the servlet and get the status. The thread just runs like 3 minutes, crawling some last.fm data.
What you need here is Session listener. The method sessionCreated() will be called only once for every browser session. So, even if the user refreshes the page, there will be no issues.
You can then go ahead and start the thread for every sessionCreated() method call.
Implement javax.servlet.SingleThreadModel => the service method will not be executed concurrently.
See the servlets specification.
Hypothetically it could be implemented by creating a Map<String,Thread> and then your servlet gets called it tries to look up the map with the sessionId.
Just a sketch:
public class LoaderServlet extends HttpServlet {
private Map<String,Thread> threadMap = new HashMap<>();
protected void doPost(..) {
String sessionId = request.getSesion().getId();
Thread u = null;
if(threadMap.containsKey()) {
u = threadMap.get(sessionId);
} else {
u = new Thread(...);
threadMap.put(sessionId, u);
}
// use thread 'u' as you wish
}
}
Notes:
this uses session id's, not users to associate threads
have a look at ThreadPools, they are great
as a commenter pointed out: synchronization issues are not considered in this sketch
Your first task is to figure out how to identify users uniquely, for instance how would you discern different users behind a proxy/SOHO gateway?
Once you have that down it's basically just having a singleton object serving a user<->thread map to your servlet.
And then we get into the scalability issue as #beny23 mentions in a comment above... I absolutely concur with the point made - your approach is not sound scalability-wise!
Cheers,
As I understand, you want to avoid parallel processing of requests from the same user. I'd suggest you other approach: associate lock with each user and store it in session. And before start processing of users request - try to get that lock. So current thread will wait while other requests from this user are handling. (Use session listener to store lock, when session is created)