i am working on a GWT web application and i was wondering if there is a way to keep some classes alive on the server.
The point is that i would like to create a static class that will run on the server and manage something for me.. lets say currently logged in users.
public class someclass{
private static someclass instance;
private List<user> users;
private someclass(){
users = new ArrayList<user>();
}
public someclass getInstance()
{
if (instance == null)
instance = new someclass();
return instance;
}
public addUser(user u)
{
users.add(user);
}
}
assume that there is no sync needed its not impotent for the question.
lets say first user logs in, and is added to the users list.
when a second user logs in. will the user list be empty? or will it have the old user as well?
Thanks...
There are different ways to do this. However, a very simple way to tackle this scenario would be to use a startup servlet
In web.xml define it as follows.
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>TestServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Maintain the stat in static fields of that servlet.
While the answer of ring bearer is the most compatible (it will work even in some very old servlet containers), I think the current approach would be to register a context listener for your application. As an example, I use this in my web.xml in order to have the Quartz task scheduler available:
<listener>
<listener-class>org.quartz.ee.servlet.QuartzInitializerListener</listener-class>
</listener>
Your context listener must implement the javax.servlet.ServletContextListener interface.The servlet container will call void contextInitialized(javax.servlet.ServletContextEvent sce) to notify application startup. On shutdown, the servlet container will call void contextDestroyed(javax.servlet.ServletContextEvent sce).
The approach with servlets is similar but your logic would be in the servlet lifecycle operations.
Related
I have a 3-tier application I have to modify. I'm totally new to the entire webstuff of Java, so bear with me please.
Currently the application has a UI, Application and Database layer but I am attempting to decouple the SQL database from the database layer using dependency injection.
So at some point I'm not going to need SQL server credentials in my application because the database backend might be plain text.
The point is that the current SQL credentials are stored in the web.xml file as init-parameters. These are fetched in servlet code as follows:
String cfgfile = getInitParameter("config_file");
properties.load(new FileInputStream(cfgfile));
//Some code..
properties.getProperty("dbUser");
This happens in the front-end, is passed to the applicationlayer constructor, which passes it on to the database constructor.
So this works, but the credentials are just passed along to the data access layer to then create a SQLDatabase.
So I figured I'd just pull these credentials in inside the SQL specific class. However, I'm stuck on how to get them out of the web.xml file.
I tried using getServletContext() but that doesnt seem to work.
I can imagine that there is no notion of any servlet at the DAL level, so I'm stuck on how to fix this.
Register ServletContextListener to load Init parameters at server start-up.
Load properties and make it visible to other classes statically.
Sample code:
public class AppServletContextListener implements ServletContextListener {
private static Properties properties;
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
String cfgfile = servletContextEvent.getServletContext().getInitParameter("config_file");
properties.load(new FileInputStream(cfgfile));
//Some code..
properties.getProperty("dbUser");
}
public static Properties getProperties(){
return properties;
}
}
web.xml:
<listener>
<listener-class>com.x.y.z.AppServletContextListener</listener-class>
</listener>
<context-param>
<param-name>config_file</param-name>
<param-value>config_file_location</param-value>
</context-param>
You are correct that the web.xml seems the wrong place to be defining database credentials.
It sounds like you really want to define the database credentials as properties and inject them directly into your data access layer.
As you are using Spring you might want to consider defining a DataSource within your context.xml and either defining the credentials directly in there or using a properties file. Have a look at the Spring documentation for more details.
For the current project I'm working on, I've decided to use the front controller pattern. I've always been under the impression that a front controller should (ultimately) be responsible for everything that happens in a web app. Would listeners violate this pattern?
public class MyDatabase implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
...
String driver = context.getInitParameter("driver");
}
}
This, of course, seems a lot simpler than:
public class FrontController extends HttpServlet {
public void service (....) {
MyDatabase db = new MyDatabase(context.getInitParameter("driver"));
}
}
This is a very simplified example; there would be more parameters in practice. So, which snippet would be considered more faithful to the front controller pattern – passing the config from FrontController down, or supplying the config directly to the classes?
As I am new to Java, I am trying to learn servlets without using a framework (for the time being).
Main intent for Front controller is to provide a centralized entry point for handling requests and consequently
to control navigation across a set of related pages (for instance,
multiple pages might be used in an online purchase) from a front
controller than it is to make the individual pages responsible for
navigation
Separating responsibility of initialization of resources from Front controller pattern is good, and you choose a right place for this since ServletContextListener is responsible for receiving notification events about ServletContext lifecycle. Code inside ServletContextListener class will run before the web application starts.
I'm creating a GWT application that will be accessed by a POST request, which contains parameters I care about (user id, etc.).
All the reading I've done so far has led me to believe that I should create a servlet (I'm using tomcat) that will handle the POST parameters and then forward to my GWT application. I've gotten this working, but I'm still having trouble passing this data to my application. I've seen 3 suggested approaches:
Save data to context: I have this working right now, but I'm not happy with it. When the servlet is accessed, I parse the parameters and update the context of my GWT web application and then forward to the application where I make an RPC call to read the context. This does what I want it to, but this creates a race condition when multiple users try to access the application at the same and the context is rapidly changing.
Store data in session: I've tried saving the data to the request session in my servlet, and then accessing the session in my RPC, but I always get a new/different session, so I assume I'm mucking this up somewhere.
Save session on servlet
HttpSession session = request.getSession();
session.setAttribute("test", "testValue");
response.sendRedirect(response.encodeRedirectURL("/GWT_Application"));
Access session in RPC
HttpSession session = this.getThreadLocalRequest().getSession();
session.getAttribute("test");
This returns a different session, which results in the "test" attribute being null.
Pass data in URL: My application will be opened in an iframe, meaning Window.location.getParameter() will not be usable.
Any help would be greatly appreciated! I'm still learning GWT and web development in general so don't be afraid to call me out on any obvious or silly mistakes.
Thanks!
SOLUTION
I figured out what the issue was with my session approach: the servlet in which I was previously trying to save the session data was in a separate tomcat web app from my GWT application. Moving them to the same web app solved my problems and it now works. I'm not sure, but I'm guessing that this was a problem because redirecting to another web app switches the context. I'll outline my whole approach in the hopes this saves someone else some time later:
Put your servlet code in the server folder of your GWT project:
package GWTApplication.server;
public class myServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
session.setAttribute("myAttribute", request.getParameter("myParam");
// handle rest of POST parameters
response.sendRedirect(response.encodeRedirectURL("/GWTApplication");
}
}
Map servlet in your GWT application's web.xml:
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>GWTApplication.myServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/myServlet</url-pattern>
</servlet-mapping>
This servlet should now be accessible at .../GWTApplication/myServlet
Next make a standar RPC. Within whatever method you will be calling in the ServiceImpl class on the server:
HttpSession session = this.getThreadLocalRequest().getSession();
return session.getAttribute("myAttribute");
Finally, make your RPC call in the onModuleLoad() method of you GWT application. As a recap:
Send the original POST request to the servlet
Save POST parameters to session variables
Redirect to GWT application
Make RPC call in onModuleLoad()
Read session variables in ServiceImpl class
You can talk with servlets through RPC call in GWT
You need to make a RPC call in the starting point of GWT application.
Set that data to serverside session and get the session data in servceImpl call of GWT which extends to RemoteServiceServlet.
Example :
YourServiceImpl extends RemoteServiceServlet {
#ovveride
doGet(){
//you can access session here
}
#ovveride
doPost(){
//you can access session here
}
#ovveride
doPut(){
//you can access session here
}
----your other methods
}
A brief Example I wrote here:How to make an GWT server call(GWT RPC?)
Since RemoteServiceServlet extends HttpServlet, you can just override doPost() method to access your POST requests. Don't forget to call super.doPost() EDIT: This doesn't work because the method is finalized in AbstractRemoteServiceServlet so it cannot be overridden.
Also, GWT Servlets POST data using the proprietary GWT RPC format. Read more about that format and how to interpret it here: GWT RPC data format
EDIT
There are several methods you can override in your ServiceImpl class that extends RemoteServiceServlet:
public String processCall(String payload) will give you a String representation of the incoming request.
protected void onAfterRequestDeserialized(RPCRequest rpcRequest) will give you a RPCRequest object that has an array of parameters, along with the method that was called.
public void service(ServletRequest request, ServletResponse response) will give you all the Attributes of the HTTP request.
I want to develop a utility class, that can be used in a static manner (static methods), for an enterprise Java system (JSP, Servlets, EJBs). It'd contain some methods that is capable to access the HttpSession object of the user and retrieves some useful info that is already stored as attributes (such as the current user id).
Some of you may wonder why do I need such thing, while I can simply passing the HttpSession object to anywhere. Actually, I am working on a legacy Java EE 5.0 system and some of the utility classes (not Servlets, JSPs, nor EJBs) have no access to the HttpSession object.
Is it possible to implement such utility class? Some constraints I/you should consider here:
The servlets/JSPs are hosted on machines that are different from what host EJBs.
The system is running on Weblogic 10.3.0.
On weblogic, there are many servers (that host servlets/JSPs) and they are under the same cluster. The same thing is with EJBs servers.
If I declare some static Collection inside the utility class, is it going to work? or maybe there will be more than one copy of it because of the multiple class loaders and multiple JVMs?
Maybe I should use a shared file or shared database to implement it? How even could I track which user invokes the utility class? Maybe tracking the thread? or maybe something related to the transaction?
Make use of ThreadLocal. You should only not store the HttpSession directly in there. The service layer should not have any dependency on javax.servlet API. Instead, extract the desired information from the HttpSession directly and store it there.
E.g. when you want to expose User attribute of the HttpSession as a thread local variable:
public class SomeContext {
private static ThreadLocal<SomeContext> instance = new ThreadLocal<SomeContext>();
private User user;
private SomeContext(User user) {
this.user = user;
}
public static SomeContext getCurrentInstance() {
return instance.get();
}
public static SomeContext newInstance(User user) {
SomeContext someContext = new SomeContext(user);
instance.set(someContext);
return someContext;
}
public void release() {
instance.remove();
}
public User getUser() {
return user;
}
}
and this in doFilter() of a servlet filter:
User user = (User) request.getSession().getAttribute("user");
SomeContext someContext = SomeContext.newInstance(user);
try {
chain.doFilter(request, response);
} finally {
// It's very important to do this in finally!
// Threads are namely pooled by the container.
someContext.release();
}
in any code which is running in the very same thread after the particular filter, including EJBs, you can get the User as follows:
User user = SomeContext.getCurrentInstance().getUser();
// ...
The HttpSession is local to a Thread.
Do you really need an HttpSession or could you just do what you want with a static ThreadLocal attribute?
See: http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html
btw, Apache Shiro uses this class to store some "Session" information.
What is the best way to make threadsafe HashMap in Tomcat ?I'm going to create ConcurrentHashMap on InitServlet once on load on my application.
(<load-on-startup>1</load-on-startup>)
Requests from different threads will read and write data to my ConcurrentHashMap.
I'm not good in mutlithreading, so not sure is it approach correct?
And where is the best place to put this HashMap should i make it static ?
Thank you
Don't make it static - put it in the ServletContext via ctx.setAttribute("mapAttrKey", map). Otherwise it's fine. However it is not very common to do things like this, so please share your use-case - there might be a more proper solution.
If the "initServlet" does nothing else than webapplication initialization, then you'd rather prefer a ServletContextListenrer. Here's a kickoff example:
public class Config implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// Do stuff during webapp's startup.
}
public void contextDestroyed(ServletContextEvent event) {
// Do stuff during webapp's shutdown.
}
}
Register it in web.xml as follows:
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
That's it. You could keep of the Map as an instance variable and/or store it in the ServletContext (the application scope).
Related questions:
Using init Servlet