I have copied some code (the example is taken from
http://hmkcode.com/java-servlet-send-receive-json-using-jquery-ajax/
it works but there is something I do not understand:
I cannot understand how the "articles" retains provious values
Is it not reinitialized with each call of the servlet ?
public class JSONServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// This will store all received articles
List<Article> articles = new LinkedList<Article>();
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
// 1. get received JSON data from request
BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
String jsonString = "";
if (br != null){
jsonString = br.readLine();
}
ObjectMapper mapper = new ObjectMapper();
Article article = mapper.readValue(jsonString, Article.class);
response.setContentType("application/json");
articles.add(article);
mapper.writeValue(response.getOutputStream(), articles);
}
}
The application server usually maintains a single instance of a servlet, so declaring the List<Article> articles as attribute in your servlet class will work as a container for the entire application for your articles.
Note that this approach should be for testing purposes only. An real world application designed like this will fail because a servlet is accessed by multiple threads at the same time and several requests on the same URL attended by your servlet that try to add the data into this unsynchronized list will raise ConcurrentModificationException.
In case you want/need to store data per client (browser), use session scope. In case you want/need to store data per application (available to all clients of your application), use application scope.
More info:
How do servlets work? Instantiation, sessions, shared variables and multithreading
How to pass parameter to jsp:include via c:set? What are the scopes of the variables in JSP? (which contains an explanation on variable scopes in web applications)
Only one servlet instance is created at any given time in a web app. Because the articles field is stored as a member variable, (not local to the method), it is shared among all requests (and all threads). This is typically a very bad practice as you could run into concurrency issues with multiple threads attempting to access the same data structure at the same time and security issues with users able to access data they perhaps shouldn't have access to.
The correct way to do this would be to use session to store data that should be private to each user, or use something like Spring's SessionScopedProxy support.
I am not an expert on Java servlets, but once the servlet is initialized (a.k.a your JSONServlet class), the articles List is initialized, and subsequent calls of doPost via clients are appending to the articles list, it is not being re-initialized every time a POST is called. It will only be deleted and re-initialized when you restart your servlet.
Related
I know one Servlets instance is shared by multiple threads for handling concurrent requests. Inside the servlets, I will call other thread-safe classes: ExternalClassOne which in turn calls ExternalClassTwo.
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ExternalClassOne cOne = new ExternalClassOne();
cOne.doSomething();
//doSomething() will also use other classes like ExternalClassTwo, ExternalClassThree...
}
}
I have some questions:
How many instances of the classes ExternalClassOne, ExternalClassTwo will be created?
If they are created per thread for each request (e.g., 100 concurrent requests = 100 instances of ExternalClassOne), does making them singleton increase the performance? Does Tomcat have any "magic" to reuse thread-safe instance where possible?
Instances are created on each execution of new, as already stated in the comment above.
Careful with singletons: To obtain the instance needs a synchronized method invocation, and this ruins the responsiveness of your application.
Tomcat does not provide any such means afaik, but the Java library. You may use ThreadLocals.
Otherwise, create the (thread-safe) classes in a ContextListener on application startup and put them in the app context, so each servlet can get them from there.
I have an ajax method on my servlet that could be running at the same time for the same user. Sorry if I use the wrong words to describe the problem but it's how I understand it so far (don't know much about threading).
Anyways here's the method
private void ajaxPartidas() throws ServletException, IOException {
//Variables necesarias
DataSource pool = (DataSource) session.get().getAttribute("pool");
Recibo registro = null;
int id = -1;
try{ id = Integer.parseInt(request.get().getParameter("id"));}catch(NumberFormatException e){}
if(id > 0){
registro = new Recibo(id);
if(!registro.obtener(pool))
registro = null;
registro.setPartidas(Partida.obtenerRegistros(pool, registro.getId()));
}
response.get().setContentType("application/json");
response.get().setHeader("Content-Type", "application/json; charset=utf-8");
response.get().getWriter().print((new Gson()).toJson(registro.getPartidas()));
}
This method is being called via ajax, it works fine the 1st time it gets called, but second time (on same id) and it returns a NullPointer on the getWriter() line. I've looked around and everyone seems to pinpoint the problem to threads. Now a little bit more of context would be that everytime the servlet enters in the
doPost(request, response)
I assign a threadlocal variable declared like so in the global vars
private static ThreadLocal<HttpServletResponse> response = new ThreadLocal<>();
and I assign it the response
Home.response.set(response);
in the doPost() method.
How would I go about making the getWriter() threadsafe?
Not sure why you're assigning the response to a class level ThreadLocal? Each new user generated request has a clean request and response object. getWriter and all methods on the servlet class are threadsafe as long as you follow the correct guidelines for using a Java Servlet. A general rule with Java Servlets is that as long as you don't use class level variables, you are thread-safe.
Instead of using a ThreadLocal, you need to pass the request and response objects as parameters to your ajaxPartidas method and then call it as you normally would. So your doPost method would look like this
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ajaxPartidas(request, response);
}
The concurrency issues are already handled by the Servlet class itself, you just need to write the business logic. See this answer in a similar thread for more details on using a Java Servlet with Ajax: https://stackoverflow.com/a/4113258/772385
Tomcat creates a new Request and Response for EVERY user request. So they are already threadsafe (unless you go in and create a new Thread). Besides, make sure you are passing "id" and is getting set properly. I think it's the "registro" object on the same line as getWriter() that's causing the NullPointerException.
I'm having a problem where Exceptions are popping up in my production system but I really don't have good information about who is causing them. The person's username is stored as a variable in their tomcat session, which I have access to in my doPost or doGet method obviously, but unless I pass that information down as a parameter to each of my business objects, I don't have access to the session. For obvious reasons, I'd like to tack the username into the logging message so I have an idea of what is going on.
So my solution is to do something like this
public class ExceptionUtil {
private ExceptionUtil() { } // no instantiation
private static final ThreadLocal<String> local = new ThreadLocal<String>();
public static void set(String user) { local.set(user); }
public static String get() { return local.get(); }
}
Then in my posts/gets, I can do this
String username = request.getSession().getAttribute("username");
ExceptionUtil.set(username);
Then in my exceptions, I might do this (contrived, bad practice example)
catch(SQLException e) {
logger.error(ExceptionUtil.get() + " did something dumb in sql", e);
throw e;
}
The only problem I'm concerned about is how Tomcat will manage my threads. What if they keep the threads? Will they persist? Will the ThreadLocal values also persist? If I was storing the entire Session in the ThreadLocal instead of just a String, that would be a serious memory leak potential. It also means if someone forgot to re-set (or forgets to clear when done) the username/session on a thread that persisted for multiple requests, there might be stale data in there.
Call my cynical, but I don't want to have to rely on programmers (even, especially myself!) not forgetting to do things for a program's correctness. If I can idiot-proof my code, I'd like to. And that means getting a better understanding of how Tomcat will use the threads.
So, the question in a single-sentence form:
If I use ThreadLocal in a webapp running on Tomcat (7.0.27), do I run
the risk of a Thread being used for multiple requests, and with it
data from a previous request being persisted?
I should note that even though they don't answer the exact question of "Tomcat/ThreadLocal shenanigans", I am open to alternative solutions that allow me to elegantly access session variables for logging purposes. I am also open to commentary about potential pitfalls of my solution. I have a business problem to solve, and I'm not married to any one solution. I just want to know who keeps causing the exceptions on my prod system :)
Yes, tomcat uses the ThreadPool concept , that means the threads are being reused and hence as you suggested "Your Thread Local retains the values" ,
alternatives what i would suggest could be
clean up threads after you are done, somewhere in the view controller
Write a Request Filter and on start of filter Clean up everything and push new values,
and assign this to every url pattern on ur server.
for the approach you are following instead of saving certain values in classes,
Store the request in Thread Local and then use the request to pull values out of session using a homemade util class, that takes request and then returns you desired value, that way you save yourself of saving session in Thread and get the value, but please ensure that u add fresh every time and clean up request after you are done(use 2nd option for that ) .
You don't need to reinvent the wheel, the log system does it for you.
If logback/log4j is your logger implementation, then Mapped Diagnostic Context(MDC) is definitely your answer.
MDC is logically like ThreadLocal, but it's better:
MDC handle thread-safe and synchronization transparently
A child thread automatically inherits a copy of the mapped diagnostic context of its parent. So even you using multi-thread to process request, it's still ok.
So set MDC in servlet filter like this, to achieve your goal:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
boolean successfulRegistration = false;
HttpServletRequest req = (HttpServletRequest) request;
Principal principal = req.getUserPrincipal();
// Please note that we could have also used a cookie to
// retrieve the user name
if (principal != null) {
String username = principal.getName();
successfulRegistration = registerUsername(username);
}
try {
chain.doFilter(request, response);
} finally {
if (successfulRegistration) {
MDC.remove(USER_KEY);
}
}
}
private boolean registerUsername(String username) {
if (username != null && username.trim().length() > 0) {
MDC.put(USER_KEY, username);
return true;
}
return false;
}
Then in your log configuration, add %X{USER_KEY} in your pattern layout to use the value you set in MDC.
In logback, there are out-of-box filter MDCInsertingServletFilter can log more information like remoteHost/requestUrl and etc, very useful information for logging.
Check the logback document on MDC http://logback.qos.ch/manual/mdc.html
I need to write a small Java class that will enable me to add to and read from the current user session.
Everything I see refers to Servlets but I'd ideally like to just use a plain old class.
Can anyone please help this Java Newbie?
Thanks
The general concept of a "Session" is really just a data storage for an interaction between a HTTP client and server. Session management is automatically handled by all HTTP Servlets. What framework?
If you're just wanting to store information for a console app to remember information between runs, then consider using Xml to save/load data from a file.
Use a component based MVC framework which abstracts all the ugly Servlet API details away so that you ends up with zero javax.servlet imports. Examples of such are JSF2 and Struts2.
In JSF2 for example, you'd just declare User class as a session scoped managed bean:
#ManagedBean
#SessionScoped
public class User {
// ...
}
Then in the "action" bean which you're using to processing the form submit, reference it as managed property:
#ManagedBean
#RequestScoped
public class SomeFormBean {
#ManagedProperty(value="#{user}")
private User user;
public void submit() {
SomeData someData = user.getSomeData();
// ...
}
}
That's it.
If you'd like to stick to raw Servlet API, then you've to live with the fact that you have to pass the raw HttpServletRequest/HttpServletResponse objects around. Best what you can do is to homegrow some abstraction around it (but then you end up like what JSF2/Struts2 are already doing, so why would you homegrow -unless for hobby/self-learning purposes :) ).
Yes, just pass the HttpRequest to your class from your servlet.
In your servlet do something like this,
cmd.execute(request);
In your class do something like this,
public class Command implements ICommand {
.
.
public void execute(HttpServletRequest request){
HttpSession sess = request.getSession(false);
}
.
.
.
}
In general, as mentioned in the other answers, session in many ways acts as a store. So to interact wth a session from another class which is outside of the Servlet/JSP framework the reference to the session in question needs to be procured. There are few ways it can be achieved:
1) Passing the session as part of a method parameter (already mentioned in other answers)
2) Binding the session to a thread local variable on to the current thread executing (refer ThreadLocal). This method has the advantage of not declaring specific parameters on the method signature of the class that needs to use the session. In addition, if the calling thread goes through a library and then again calls some specific class e.g. Servlet->YourClass0 -> Apache Some Library -> YourClass1, the session will also be available to YourClass1.
However, the thread local also needs to be cleared when the executing thread returns through the initial component (servlet let's say) otherwise there certainly could be memory leaks.
In addition, please refer to your specific framework for treatement of sessions, the above mechanism works fine in Tomcat.
I have a J2ee application where I basically want two objects, created by two separate servlets to communicate directly and I need these intances to be stable, i.e. to "know" each other during the session.
The sequence is roughly:
Client sends a request to Servlet #1, which creates object A
Client sends a second request (after the first returns) to servlet #2 which creates object B.
Object B finds A, using JNDI, and the two objects interact.
The client now continues to send requests to object A which needs to find B again.
How do I make sure that these two instances know each throughout the session?
Binding them to JNDI doesn't entirely solve the problem, since object B needs to communicate with its original servlet (servlet #2), which is not kept stable across requests.
Any ideas?
Thanks in advance.
Yes, I admit the problem description is a bit vague. But it's not a very simple application.
Still, I will try to ask it better:
My end goal is to create a sort of a "semantic debugger" for my application that, as opposed to a java debugger which simply debugs the java statements.
The application debugged is basically a servlet. which my tool connects to.
The tool maintains a connection to the application through another servlet which controls the debugging process.
These two servlets need to communicate with each other constantly and directly.
My current thought is to set up a stateful session bean that will facilitate this communication (never done it, still struggling with setting it up).
But I would appreciate any thoughts on how to achieve this better.
And what stops you from using the Session? You don't need JNDI, just place your object into session under a predefined name. If the communication object is application-wide, use Singleton.
P.S. It looks to me you're doing something weird, while the solution could in fact be simpler. Can you describe the task, not the proposed implementation? What is a "semantic debugger" anyway?
To be honest: I don't fully understand what you are trying to achieve.
Can you perhaps try to explain the problem you are trying to solve instead of the solution?
What do these objects depend on? Are they user specific? Then put them into the session and you can retrieve them from the session again (request.getSession().getAttribute("A")).
Are they global for all users? In that case put them into a spring configuration and retrieve them from there.
Never store any information inside the servlet.
EDIT:
ok, so from what I understand storing the values in the session is imho the best way to solve this problem (in "Java-Pseudo-Code"):
public class BusinessServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res) {
HttpSession session = req.getSession(true);
BusinessCode business = session.getAttribute("A");
if (business == null) {
business = new BusinessCode();
session.setAttribute("A", business);
}
DebugObject debug = session.getAttribute("B");
if (debug == null) {
debug = new DebugObject();
session.setAttribute("B", debug);
}
business.doSomeWork(debug);
}
}
public class DebugServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res) {
HttpSession session = req.getSession(true);
DebugObject debug = session.getAttribute("B");
if (debug != null) {
debug.printDebugMessages(res);
}
}
}
Does this help?