Tomcat concurrency - java

I want to use servlet concurrently.
Of course, There is lots of questions related with this subject, but my servlet still works in sequential.
I'm using servlet with Tomcat8, which uses Jena triplestore (with Apache Jena Lib)
My servlet is called within ajax from html, and return data with response.getWriter().write() method.
Each browser is blocked while servlet is running, but it would not matter with concurrent work of servlet I think (because it is each browser's work)
I attach here my server.xml setting:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxThreads="300"
acceptCount="200"
minSpareThreads="100"
/>
and doGet method:
#Override
public void init(ServletConfig conf) throws ServletException {
System.out.println("Servlet Run");
}
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String data = request.getParameter("data");
String[] spdata = data.split("\\|");
String sen = spdata[0];
String name = spdata[1];
String id = spdata[2];
System.out.println(sen + ' ' + name + ' ' + id);
int user = id.hashCode();
System.out.println("Load Init");
DialogSystemForTomcat d = new DialogSystemForTomcat(user);
System.out.println("After Init");
System.out.println("Sentence: "+sen);
String rtn = d.runJob(name, sen);
response.getWriter().write(rtn);
System.out.println("Return: "+rtn);
}
As you can see, I call another Java class within doGet() method which really processes my input.
(real process is done within d.runjob() method)
It is working sequentially for all inputs now, but I want to work in concurrent for all inputs.
What should I change? Please help.
=========================================
After...
I worked to make process as faster as possible.
But I could not make it concurrent.

Each browser is blocked while servlet is running, but it would not matter with concurrent work of servlet I think (because it is each browser's work)
You are mistaken. Servlets are concurrent, except for the obsolete SingleThreadModel which you aren't using, and unless they contain synchronized methods or blocks, which this one doesn't, or semaphores, ditto. Ergo this code is concurrent, unless the processing in the unnamed class is synchronized or uses a semaphore of some kind.
NB You have an error in the init() method. It must call super.init(conf). See the Javadoc.

Related

Is there only one servlet object per tomcat server?

I did a simple test, and executed my test servlet below from different browsers and command line tools. Regardless where I execute it, it displays the same "hohoho time". I am absolutly fine with this, I just want to know two things:
a) if I can rely on this on different versions of tomcat (today I use version 7.54)
b) does this mean a servlet is never executed parallel in different threads (i.e. two users are requesting the very same servlet at the very same time)
#WebServlet(name="test servlet 2", urlPatterns={"/test2"}, loadOnStartup=1)
public class Test2 extends HttpServlet {
private String test = "baaa .. :-(";
#Override
public void init(ServletConfig config) throws ServletException {
DEBUG("Server starts");
this.test = "hohoho " + System.currentTimeMillis();
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletOutputStream out = resp.getOutputStream();
out.write(test.getBytes());
out.flush();
out.close();
}
}
Indeed servlet container creates only one instance of each servlet. This helps you to hold state in servlet member variables. This however does not mean that servlet container cannot use the same servlet instance to process serveral requests simultaneously. This is what servlet contains (including Tomcat) does. Therefore your servlet must be thread safe. Please refer to this article for details: http://tutorials.jenkov.com/java-servlets/servlet-concurrency.html
I have below view on your question.
a) Yes,atleast from tomcat 7 onwards.For tomcat 6(annotation support),you will have to check the specs.
b) Regarding execution in parallel thread,servlet not threadsafe by default,to ensure thread safety,Servlet service() method should not access any member variables, unless these member variables are thread safe themselves.
For more on thread safety : Servlet Thread Safety
There is only one instance of each HttpServlet and its init() method is only called once when the HttpServlet is constructed. However the doGet() is called in a different thread for each request and can happen in parallel.

How to avoid sending HTTP request from a Java web server to itself?

Real situation is like this: Java web server (Weblogic) recieves a request from user for which it has to send a ZIP archive in response. Archive has to be dynamically generated from some files user asked for and one HTML report generated by the server itself. I would like to reuse JSF servlets the server already uses in other cases to generate this report. So, basically, what I use is:
HttpURLConnection self = new URL ("http://me.myself.com/report.jsf?...").openConnection ();
String report_html = fetchHtmlFromConnection (self);
and then create the requested ZIP, including the generated HTML in it.
The question is, can I somehow avoid making an internal HTTP request (to report.jsf) in this scenario? That involves basically pointless (since application just "talks" to itself anyway) roundtrips through operating system, HTTPD (which might be on a different machine), etc.
I am not very familiar with JSF, but from what I understand of them you can use a technic that is also applicable to JSP pages:
Create your own HttpServletResponseWrapper (a class used by the container that lets you modify the response)
Use it to override the default Writer (that writes the rendered page to the output) and provide one that writes the output to a String or a temporary file that will feed the compressing code.
There is a nice and simple tutorial that shows you how to do that:
http://blog.valotas.com/2011/09/get-output-of-jsp-or-servlet-response.html
Then
As hinted by gyan, get a ServletRequestDispatcher from your servlet that will let you invoke the rendering of the JSF
Forward the servlet call in order to provide your own HttpServletResponseWrapper
Use your HttpServletResponseWrapper to get the rendered HTML and feed it to the zipping code.
So the zipping Servlet would be like:
TempFileRespWrapper respWrapper = new TempFileRespWrapper();
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher( "/report.jsf");
dispatcher.forward(request, respWrapper);
File f = respWrapper.getOutputPath();
addFileToZip(f);
You should have a business service layer so that a "generateReport" service could be used inside multiple presentation views (even the "zip file" one).
You could do this in standard J2EE way via EJBs or through any custom framework that let you specify injectable business services (e.g. spring).
I think the main problem here is that you can generate the report only through the client interface (http). That makes it an isolated service, not available to other parts of the application. Basically you need a refactoring.
Do not code business inside JSFs or similar. (by the way, try not using jsf at all :D )
BUSINESS LAYER PRESENTATION
generateReportService---|---jsf-HtmlReport
\__|___
| \
someOtherContentService-|----jsf-Zip
Think about request dispatcher strategy, where request/response object would be sent to the report servlet from the entry servlet. In turn report servlet would generate the report and control can be sent to next servlet, which completes the rest of zip and send process.
For constructing a RequestDispatcher object, you can use either the ServletRequest.getRequestDispatcher() method or the ServletContext.getRequestDispatcher() method.
RequestDispatcher dispatcher=getServletContext().getRequestDispatcher( "/report.jsf" );
dispatcher.forward( request, response );
The next servlet set mime type as 'application/zip' and write the zip binary to browser. The user's browser would handle the content in the form of download depending on the browser settings.
Make sure that your web server is configured to cache (generated) html and you will not have to worry about it. First request to full or partial URL will generate a call to jsf generator and later it will be fetched from web server cache.
Yes, there will be a little overhead involved
(your source -> we server -> generator page or cache)
But it is going to be easier in the end.
Tested Solution!
This solution actually get ideas already posted here (specially from #gyan).
Write a Servlet to zip
(you could use an filter for that too. For example, suppose that you have a ZipFilter. You could map the filter for all *.zip and chain that filter with a URLRewrite filter for respective .jsf URL).
public class ZipServlet
extends HttpServlet {
#Override
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException,
IOException {
ZipEntry zipEntry = new ZipEntry("helloWorld.html");
ZipHttpServletResponseWrapper respWrapper = new ZipHttpServletResponseWrapper(response, zipEntry);
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/helloWorld.jsf");
dispatcher.forward(request, respWrapper);
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "inline; filename=output.zip;");
response.flushBuffer();
respWrapper.getOutputStream().close();
}
}
NOTE : Yes, you should use RequestDispatcher
ZipHttpServletResponseWrapper
There's no much to say about that. From Java 7, you can use a native Class to create zip files properly. Using Decorator pattern with ZipOutputStream on top of response.getOutputStream() should be recommended way.
Remember that HttpServletResponseWrapper is a decorator. You should not use that if you don't want to reuse the target "servlet" output directly (you could use an stunt HttpServletResponse rather than use HttpServletResponseWrapper).
public class ZipHttpServletResponseWrapper
extends HttpServletResponseWrapper {
private ZipEntry entry;
private ZipServletOutputStreamWrapper streamWrapper;
private ZipOutputStream outputStream;
private PrintWriter printWriter;
public ZipHttpServletResponseWrapper(HttpServletResponse response, ZipEntry entry) {
super(response);
this.entry = entry;
}
#Override
public ServletOutputStream getOutputStream() throws IOException {
if (streamWrapper == null) {
outputStream = new ZipOutputStream(this.getResponse().getOutputStream());
outputStream.putNextEntry(entry);
streamWrapper = new ZipServletOutputStreamWrapper(outputStream);
}
return streamWrapper;
}
#Override
public PrintWriter getWriter() throws IOException {
if (printWriter == null) {
printWriter = new PrintWriter(getOutputStream());
}
return printWriter;
}
private class ZipServletOutputStreamWrapper
extends ServletOutputStream {
private ZipOutputStream outputStream;
public ZipServletOutputStreamWrapper(ZipOutputStream outputStream) {
this.outputStream = outputStream;
}
#Override
public void close() throws IOException {
outputStream.closeEntry();
outputStream.finish();
}
#Override
public void write(int b) throws IOException {
outputStream.write(b);
}
}
}
Now, the secret: mapping wisely!
Some important parts of JSF could use Filter chain (for example, myfaces from Apache use some extensions to provide some JSF components). In this case, you should specify in these filters digest FORWARD and REQUEST
<filter-mapping>
<filter-name>extensionsFilter</filter-name>
<url-pattern>*.jsf</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>

Naming Tomcat worker threads

Is there a simple way to change the default name that Tomcat gives its worker threads? Ideally, I'd like all the threads associated with a certain web application to have it in their name, perhaps as a prefix. For example, if I deploy app1.war and app2.war, I'd like all the threads for app1 to have app1 in their name.
From a forum, reposted here with a bit better formatting; a simple filter, that renames each thread to the request URI, in this example:
public class ThreadNameFilter implements javax.servlet.Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain fc) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest)req;
final Thread curThr = Thread.currentThread();
final String oldName = curThr.getName();
// change the name of the current thread to something related
// to the application (e.g. URI)
try {
curThr.setName(httpReq.getRequestURI());
fc.doFilter(req, resp);
} finally {
curThr.setName(oldName);
}
}
}
Threads are only temporarily "associated" with a particular webapp.
I can't imagine why this would be something useful at all:
Logging should include context information (e.g. what component is logging)
Profiling can easily reveal what code is being executed (and thus reveal the webapp that is "running")
Exceptions include full stack traces and can reveal what code was being executed (and thus reveal the webapp that was "running")
Long-running threads can be inspected via a thread-dump (and thus reveal the webapp that is "running" in the long-running request)

java tomcat utf-8 encoding issue

I am developing a simple web application using java/jsp/tomcat/mysql, and the most problem lies on the character encoding because I need to deal with UTF-8 encoding instead of the default 8851.
First of I'd like to describe my program structure. I am using a Servlet called Controller.java to handle all request. So in web.xml, I have a Controller servlet which takes all request from *.do.
Then this Controller will dispatch the request based on the requested URL, for example, if client asks for register.do, Controller will dispatch the request to Register.java.
And in the Register.java, there is a method which takes the request as parameter, namely:
public String perform(HttpServletRequest request) {
do something with the request...
}
So the problem is if I want to print something in UTF-8 inside this method, it will give random characters. For example, I have an Enum which stores several constants, one of the properties the Enum has is its name in Traditional Chinese. If I print it in
public static void main(Stirng[] args{
System.out.println(MyEnum.One.getChn());
logger.info(MyEnum.One.getChn());
}
This is printed correctly in Chinese. However, if I put the exact code inside the method dealing with HttpServletRequest:
public String perform(HttpServletRequest request) {
System.out.println(MyEnum.One.getChn());
logger.info(MyEnum.One.getChn());
}
They are printed as random characters, but I can see from the debug window (eclipse) that the variables are holding correct Chinese characters.
So, the same situation happens when I want to store the value from request.getParameter(). In the debug window, I can see the variable is holding correct characters, but one I print it out or try to store it in the database, it is random characters.
I don't know why the behavior acts like this, and this is blocking me from reading submitted form values and store them into database. Could someone give some hints on this?
Great thanks.
Here is a small tutorial what you need to do to make UTF-8 work in your web application:
You have to implement Filter in your application for character encoding:
public class CharacterEncodingFilter implements Filter {
#Override
public void init(FilterConfig filterConfig)
throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
servletRequest.setCharacterEncoding("UTF-8");
servletResponse.setContentType("text/html; charset=UTF-8");
filterChain.doFilter(servletRequest, servletResponse);
}
#Override
public void destroy() {
}
}
You have to make sure that your tomcat's server.xml's file connector element has URIEncoding attribute which value is UTF-8.
<Connector port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
URIEncoding="UTF-8"
redirectPort="8443"/>
Also you need to specify this in every JSP page:
<%#page contentType="text/html" pageEncoding="UTF-8"%>
If you need to use UTF-8 encoding (and really, everybody should be going this these days), then you can follow the "UTF-8 everywhere HOWTO" found in the Tomcat FAQ:
http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q8
Remember that you also need to support UTF-8 in your database's text fields.
Also remember that sometimes "printing" a String with non-ASCII characters in it to a log file or the console can be affected by
The character encoding of the output stream
The character encoding of the file reader (e.g. cat/less/vi)
The character encoding of the terminal
You might be better off writing the values to a file and then using a hex editor to examine the contents to be sure that you are getting the byte values you are looking for.

How do I specify a query string in Tomcat's <servlet-mapping> <url-pattern>?

I am running Tomcat 5.5.4 and have a servlet running with no problems. However, I'd like to set up a mapping to only launch the servlet when a URL containing a particular query string is submitted.
Right now in web.xml I have:
<servlet-mapping>
<servlet-name>MyServer</servlet-name>
<url-pattern>/go/*</url-pattern>
</servlet-mapping>
If a browser submits http://localhost/MyServer/go?P=123 the servlet is launched and all is well. However, I'd like to only launch that servlet if the URL is exactly as just shown. Unfortunately, right now if the URL is http://localhost/MyServer/go?P=AnyDarnThing the servlet still launches. I have tried setting up the following:
<url-pattern>/go?P=123</url-pattern>
but this results in The requested resource (/MyServer/go) is not available.
I've tried numerous variations (quoting the string, ...) on the above URL pattern but I always get the above error. I notice that if I (for debugging purposes) drop the "?" as in
<url-pattern>/goP=123</url-pattern>
I no longer get the error message and the server launches (but, of course, it doesn't respond to the "query string" because it's not properly formed.) This suggest to me that the "?" is causing a problem in the mapping. I've tried replacing it with its URL special character equivalent as follows:
<url-pattern>/go%3FP=123</url-pattern>
but this gives the same result just described above when I tried dropping the "?" altogether.
I realize I can let the servlet get launched when any query string is submitted and then "ignore" the request for all but the one I care about but there is a reason I'd prefer to not have the servlet launched to begin with. So, my question is, how can I configure the servlet so that it is only launched when a specific query string is included?
Thank you.
You can't do that. The url-pattern is pretty limited.
If you want to have distinct actions taken based on a GET parameter, you can do that manually. In the doGet() method of the servlet have a simple if-clause and invoke different methods depending on the query string / get param.
You can't do that using URL patterns.
You can achive this using filters. Implement a filter which will forward to the Servlet only if the query params exists.
Here is the how the filter will look like:
public class ServletAcessFilter implements Filter
{
public void init(FilterConfig filterConfig) throws ServletException
{
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException
{
//validate the request, check if the request can be forwarded to servlet.
if(request.getParameter("P").equalsIgnoreCase("123")){
filterChain.doFilter(request, response);
} else {
//write what you want to do if the request has no access
//below code will write 404 not found, you can do based on your requirement
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(404);
}
}
public void destroy()
{
}
}
Define the filter in the web.xml like this:
<filter>
<filter-name>ServletAccessFilter</filter-name>
<filter-class>com.ServletAcessFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ServletAccessFilter</filter-name>
<url-pattern>/go/*</url-pattern>
</filter-mapping>
To add to Bozho response, you may also try to move to Clean URLs
This will greatly increase your options in terms of URL pattern matching, and, in particular, may significantly ease configuration of a fronting reverse proxy if you ever need one.

Categories

Resources