I want to display a default custom page until my server starts completely.
Problem Statement : I am doing few setup things when the server starts, so it takes a very long time for the server to start. So in the meantime server stats if some user hits the home page url, so he should see some default page showing server is starting..blah blah...
Now the issue is if server is not started completely, neither my application context is loaded fully, and the response for the hit is "aborted", which is not a HTTP response, so how can i catch this response and show some error page.
Server can be jetty, tomcat any
A quick and dirty example:
public class DeferringServlet extends HttpServlet {
private static final Logger logger = Logger.getLogger(DeferringServlet.class);
private MyApp myApp;
private Thread appInitializerThread;
public void init() {
myApp = new MyApp();
appInitializerThread = new Thread(new Runnable() {
public void run() {
myApp.init();
}
});
appInitializerThread.start();
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
if(myApp.isInitialized()) {
myApp.doGet(req, resp);
} else {
myApp.setStatus(503); //Service unavailable
myApp.getWriter.println("Please wait. App is loading");
}
}
public void destroy() {
if(appInitializerThread.isAlive()) {
appInitializerThread.interrupt();
}
try {
appInitializerThread.join();
} catch (InterruptedException ex) {
logger.warn("Interrupted before app initializer could finish");
}
}
}
Related
I am running an application using tomcat as the container - at start up, several files need to be found and loaded. However, if one of these files doesn't exist or can't be read, I want to log the exception and exit the app, which I am currently doing using System.exit(1)... however, is there a better way of doing this?
Any help is much appreciated!
I dont know if this fits your needs but it actually worked for my app. The listener is
called at application start, if it is declared in your web.xml:
<listener>
<listener-class>your.package.TestServletListener</listener-class>
</listener>
There you can do testing and call the ShutdownThread if one fails. It will connect to Tomcats shutdown port and send the shutdown command as a String:
public class TestServletListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("Starting app, running 5 tests ...");
// do tests ...
for (int i = 0; i < 5; i++) {
System.out.println("testing ... " + i);
waitFor(1000);
}
// If a test failed call:
System.out.println("test failed!");
new ShutdownTask().start();
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.print("Stopping app, cleaning up (takes 3 sec) ... ");
waitFor(3000);
System.out.println("done");
}
private void waitFor(int i) {
try {
Thread.sleep(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class ShutdownTask extends Thread {
#Override
public void run() {
try {
Socket s = new Socket("127.0.0.1", 8015);
PrintStream os = new PrintStream(s.getOutputStream());
os.println("shutdown");
s.close();
System.out.println("Shutting down server ...");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
You need to make sure that the shutdown port and shutdown command are in sync with your Tomcats server.xml:
...
<Server port="8015" shutdown="shutdown">
...
For example, you could pass them as context parameters in your web.xml. Like System.exit(...) this is not going to work (without further config) if Tomcat is running with a SecurityManager.
You should consider embedding Tomcat, i.e. have your AppStarter class perform those checks and then start Tomcat:
public class AppStarter {
public static void main(String[] args) {
// Check if everything is ready...
if (file1.exists() && file2.exists() && condition3) {
// Start Tomcat here.
}
else {
System.out.println("Invalid configuration.");
}
}
}
You can find how to embed Tomcat tutorials on the Internet.
I have an web application which accept some data from user to create a task, then the task should be executed.
Since the execution of the Task is to download something from the internet which will cost some time, so I tried to create a new Thread to do the job.
This is my idea:
create a LoaderThread used to download data. And the LoaderThread hold a field of ArrayList used for put the Task.
A Servlet to handle the request and response.
When the Servlet startup, start the LoaderThread
During the servlet run, add task to the LoaderThread.
This is the code(some of them is omitted):
public class RwdServlet extends HttpServlet {
private StaticMapLoader loader;
#Override
public void init() throws ServletException {
super.init();
loader = new StaticMapLoader();
loader.startRunning();
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Task t=createTask(req);
loader.addTask(t);
}
#Override
public void destroy() {
loader.stopRunning();
}
}
public class StaticMapLoader extends Thread {
private List<Task> tasks = new ArrayList<Task>();
private boolean running = false;
#Override
public void run() {
while (running) {
if (tasks.size() > 0) {
Task t = tasks.get(0);
log.info(t);
if (t != null && t.status == Status.waiting) {
tasks.remove(0);
t.status = Status.running;
downLoad(t);
}
}
}
}
private void downLoad(Task t) {
//download file
}
public void addTask(Task t) {
tasks.add(t);
}
public void startRunning() {
running = true;
this.start();
}
public void stopRunning() {
running = false;
this.interrupt();
}
}
The above code worked, but I found that even the tasks were empty and there are no new task added, the loop will keep running.
So I though if I can make the LoaderThread suspend when there are no tasks, and notify it when new task come out.
So I tried this:
#Override
public void run() {
while (running) {
if (tasks.size() > 0) {
Task t = tasks.get(0);
log.info(t);
if (t != null && t.status == Status.waiting) {
tasks.remove(0);
t.status = Status.running;
downLoad(t);
}
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I tried to call the wait() if the tasks is empty.
But I do not know how to wake up it?
Also, is there anything I should know to improve the application?
BWT, is it possible that more than one LoaderThread instance will be created? If so , how to avoid it?
It seems that I can use other implementation, but I wonder if my case is refactor-able?
Since I want to learn some thing I have missed. :) Thanks.
Your requirements are the standard usage of ExecutorService, so I would recommend you to use ExecutorService and not reinvent the wheel.
Base on code you provided, your servlet should look like this:
public class RwdServlet extends HttpServlet {
private ExecutorService loader;
#Override
public void init() throws ServletException {
super.init();
loader = Executors.newCachedThreadPool();//or use some other executor, google about difference between them
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Task t=createTask(req); //assume that task implements Runnable or Callable
loader.submit(t); // submit a task to executor after this line your task will start execution in another thread
}
#Override
public void destroy() {
loader.shutdown();//this will destroy executor service but before that it will wait until all already submitted tasks will be executed
}
}
See link with example
Your use case calls for an ExecutorService and you have started reimplementing it from scratch. Better stop now and use the finished, bug-free, flexible, and powerful product from the standard library.
I have been trying to use the timeout feature of the async context. But the behavior is highly intermittent. Sometimes the timeout happens, and many a times it doesn't. I am pasting my code here.
#WebServlet(name = "TestServlet", urlPatterns = {"/test"},asyncSupported = true)
public class TestServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
private static PriorityBlockingQueue<Runnable> pq = new PriorityBlockingQueue<Runnable>(1000);
private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,1,10, TimeUnit.SECONDS,pq);
public void service(final ServletRequest servletRequest, final ServletResponse response)
throws ServletException, IOException {
TestListener listener = new TestListener();
final AsyncContext asyncContext = servletRequest.startAsync();
asyncContext.addListener(listener);
asyncContext.setTimeout(100);
Handler handler = new Handler(asyncContext);
threadPoolExecutor.execute(handler);
}
}
The listener and the handler code is included below.
public class TestListener implements AsyncListener {
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("Event completed");
}
public void onError(AsyncEvent event) throws IOException {
event.getAsyncContext().complete();
}
public void onStartAsync(AsyncEvent event) throws IOException {
// TODO Auto-generated method stub
}
public void onTimeout(AsyncEvent event){
System.out.println("Timeout ");
event.getAsyncContext().complete();
}
}
public class Handler implements Runnable {
private AsyncContext asyncContext;
public Handler(AsyncContext asyncContext){
this.asyncContext = asyncContext;
}
public void run(){
try {
long currtime = System.currentTimeMillis();
Thread.sleep(500);
System.out.println("slept for " + (System.currentTimeMillis() - currtime));
} catch (InterruptedException e) {
System.out.println("Error in thread ");
}
try{
if(asyncContext != null){
System.out.println("Completing async context " + " timeout is " + asyncContext.getTimeout());
asyncContext.complete();
}
}catch (Exception e){
System.out.println("Exception in completing async context ");
}
}
}
And the output is intermittent. Including the same here -
[ops#root combinedlogs]$ time curl "http://localhost:9001/mockresponse/test"
real 0m0.506s
user 0m0.001s
sys 0m0.003s
[ops#root combinedlogs]$ time curl "http://localhost:9001/mockresponse/test"
real 0m0.159s
user 0m0.001s
sys 0m0.003s
Catalina logs -
slept for 500
Completing async context timeout is 100
Event completed
Timeout
Event completed
slept for 500
Exception in completing async context
I don't understand why this is happening. Please help! Thanks for your time.
PS: The tomcat version is 7.0.37
Try increasing the timeout and the sleep interval to more than 1 sec. For ex: Try a timeout interval of 2sec and a sleep for 5sec. It is possible that the servlet container does not detect timeouts less than 1 sec consistently. There were couple of bugs (marginally) related to such sub-second timeouts earlier in tomcat, like this one. I understand you are using a later version of tomcat than mentioned in that bug, still its worth the try.
I was going through a expert(?) tutorial on "Asynchronous processing support in Servlet 3.0" (http://www.javaworld.com/javaworld/jw-02-2009/jw-02-servlet3.html?page=2). In it there is this following code snippet:
#WebServlet(name="myServlet", urlPatterns={"/slowprocess"}, asyncSupported=true)
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
AsyncContext aCtx = request.startAsync(request, response);
ServletContext appScope = request.getServletContext();
((Queue<AsyncContext>)appScope.getAttribute("slowWebServiceJobQueue")).add(aCtx);
}
}
#WebServletContextListener
public class SlowWebService implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
Queue<AsyncContext> jobQueue = new ConcurrentLinkedQueue<AsyncContext>();
sce.getServletContext().setAttribute("slowWebServiceJobQueue", jobQueue);
// pool size matching Web services capacity
Executor executor = Executors.newFixedThreadPool(10);
while(true)
{
if(!jobQueue.isEmpty())
{
final AsyncContext aCtx = jobQueue.poll();
executor.execute(new Runnable(){
public void run() {
ServletRequest request = aCtx.getRequest();
// get parameteres
// invoke a Web service endpoint
// set results
aCtx.forward("/result.jsp");
}
});
}
}
}
public void contextDestroyed(ServletContextEvent sce) {
}
}
Since, this was an expert 'this-is-how-it-is-done' article I am assuming that they wouldnt be writing some silly code.
But I am unable to accept having a 'while(true)' loop inside 'contextInitialized()'.
As per my understanding, this context listener thread would never exit the 'contextInitialized()' method. Right ?
What happens to any other Servlets I have in my application ? Are they free to service requests ? Or does this bring down the entire servlet container ?
1) I would change the code as following:
#WebServletContextListener
public class SlowWebService implements ServletContextListener, Runnable {
private volatile boolean running;
ExecutorService executor;
Thread runner;
Queue<AsyncContext> jobQueue;
public void contextInitialized(ServletContextEvent sce) {
jobQueue = new ConcurrentLinkedQueue<AsyncContext>();
sce.getServletContext().setAttribute("slowWebServiceJobQueue", jobQueue);
// pool size matching Web services capacity
executor = Executors.newFixedThreadPool(10);
runner = new Thread(this);
runner.start();
}
public void run() {
running = true;
while(running)
{
try {
if(!jobQueue.isEmpty())
{
final AsyncContext aCtx = jobQueue.poll();
executor.execute(new Runnable(){
public void run() {
ServletRequest request = aCtx.getRequest();
// get parameteres
// invoke a Web service endpoint
// set results
aCtx.forward("/result.jsp");
}
});
}
}
catch (InterruptException e) {
}
}
}
public void contextDestroyed(ServletContextEvent sce) {
running = false;
runner.interrupt();
executor.shutdown();
}
}
2) Other Servlets shouldn't be affected as long as they have a different urlPattern.
Is it possible to take an HTTPServletRequest away from its thread, dissolve this thread (i.e. bring it back to the pool), but keep the underlying connection with the browser working, until I get the results from a time-consuming operation (say, processing an image)? When the return data are processed, another method should be called asynchronously, and be given the request as well as the data as parameters.
Usually, long pooling functions in a pretty blocking fashion, where the current thread is not dissolved, which reduces the scalability of the server-side app, in terms of concurrent connections.
Yes, you can do this with Servlet 3.0
Below is the sample to write the alert every 30 secs(not tested).
#WebServlet(async =“true”)
public class AsyncServlet extends HttpServlet {
Timer timer = new Timer("ClientNotifier");
public void doGet(HttpServletRequest req, HttpServletResponse res) {
AsyncContext aCtx = request.startAsync(req, res);
// Suspend request for 30 Secs
timer.schedule(new TimerTask(aCtx) {
public void run() {
try{
//read unread alerts count
int unreadAlertCount = alertManager.getUnreadAlerts(username);
// write unread alerts count
response.write(unreadAlertCount);
}
catch(Exception e){
aCtx.complete();
}
}
}, 30000);
}
}
Below is the sample to write based on an event. The alertManager has to be implemented which notifies AlertNotificationHandler when client has to be alerted.
#WebServlet(async=“true”)
public class AsyncServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) {
final AsyncContext asyncCtx = request.startAsync(req, res);
alertManager.register(new AlertNotificationHandler() {
public void onNewAlert() { // Notified on new alerts
try {
int unreadAlertCount =
alertManager.getUnreadAlerts();
ServletResponse response = asyncCtx.getResponse();
writeResponse(response, unreadAlertCount);
// Write unread alerts count
} catch (Exception ex) {
asyncCtx.complete();
// Closes the response
}
}
});
}
}
Yes, it's possible using Servlet spec ver. 3.0. Implementation I can recommend is Jetty server. See here.