Multiple dynamic data sources for a servlet context - java

I'm developing a java servlet web application that manages information from multiple databases (all structurally the same) each corresponding to a different "business". The user selects "the current business" which is stored in the session and the application can display or modify that "current business".
I would like to use tomcat Resources in a dynamic way to have access to these businesses using jndi. In this way I can use the jstl sql tags or context lookups in servlets. I can not define each Resource in the web.xml file because they are stored in a SQL table. The end result is to be able to write simple jsp that has lines like these:
<%# taglib uri="http://java.sun.com/jstl/sql" prefix="sql" %>
<sql:query var = "users" dataSource="sources/${sessionScope.currentBusiness}">
select id, firstName, lastName FROM user
</sql:query>
or servlets that can have lines like these
String request.getSession().getAttribute("currentBusiness");
Context initial = new InitialContext();
Context context = (Context) initial.lookup("java:comp/env");
DataSource source = (DataSource) context.lookup("sources/" + currentBusiness);
where I can get the correct datasource for the "current business".
I have experimented with writing my own ObjectFactories derived from javax.naming.spi.ObjectFactory without success. Any pointers on how to easily do this?

I finally settled for the following solution consisting on a SessionListener and a Servlet that work as follows. The SessionListener has the following form:
public class SessionListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent event) {
HttpSession session = event.getSession();
// get list of possible data sources available to this session
List<DataSource> sources = new ArrayList<DataSource>();
... code to get the available sources
// get the current data source
DataSource source = null;
... code to get the current source
source = sources.get(0); // for example
// setup the session attributes
session.setAttribute("availableSources", sources);
session.setAttribute("currentSource", source);
}
}
Whenever a user logs in and a session is created, the list of available DataSources, and the current one, are placed into the session. This is done at the Session level because DataSources depend on the user login in. It is now possible to have access at them from within the application. To change the current DataSource I created a Servlet with this simplified version:
public abstract class BoxletServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession(true);
String s = request.getParameter("source");
// based on 's' choose from the available DataSource
List<DataSource> sources = (List<DataSource>) session.getParameter("availableSources");
Source source = chooseFrom(sources, s);
session.setParameter("currentSource", source);
// forward to a page saying that the DataSource changed
}
}
With this implementation it is now possible to create the following jsps:
<%# taglib uri="http://java.sun.com/jstl/sql" prefix="sql" %>
<sql:query var = "users" dataSource="${sessionScope.currentSource}">
select id, firstName, lastName FROM user
</sql:query>
Hope it helps someone else.

Create the data sources in a ServletContextListener and place them in the ServletContext.

This approach will certainly "work", but the notion of a separate, identical database for each business seems wrong to me. Surely being able to delineate business somewhere in the schema seems possible. Separating them this way requires a new database per business, where a schema would only require a new business identifier.
I'd also argue that any possibility of cross-business data mining is lost, unless you ETL data from separate databases into a dimensional cube for ad hoc reporting and querying.
And JSTL <sql> tags should only be used in the simplest web apps. You leave yourself open to the possibility of SQL injection attacks when you forego validation on a middle tier.
UPDATE:
You have to declare resources in your web.xml AFAIK, so whenever you have a new database you have to stop the application, configure the new JNDI source, and restart Tomcat. I hope you're clustered, because all the previous clients will be affected by the app being down every time you add a new business/database.

Related

Best practice to store shared data between pages and how to control it

I am building Fusion web application using ADF Technology in Jdeveloper 11.1.2.3. Some time we need to store shared variables among pages like employee id, employee role and some time Boolean variables used as flag attributes.
My Question is what is the best practice to do that? Now I am using the session scope to store these data but I am not sure if it is the preferred way or not.
For example if I want to store some thing in the session I am doing the following:
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ectx = fc.getExternalContext();
HttpSession userSession = (HttpSession)ectx.getSession(true);
userSession.setAttribute("userID", [user id attribute goes here]);
If I want to get it back I am doing the following:
Map sessionScope = ADFContext.getCurrent().getSessionScope();
[data type] userIDFromSession = ([data type])sessionScope.get("userID");
But I do not know how to clear the session during execution and I do not know if there is better way to do the same thing.
If these are values from the page, why are you accessing them from the backing Java code? Why not use Expression Language?
If the pages are in the same Task Flow then you should use PageFlowScope. Create a bean, MyBean, and access like this in the Expression Language:
#{pageFlowScope.myBean.attribute}
When page is submitted, getAttribute is called automatically. When page is rendered, setAttribute is called automatically.
To acccess beans from the backing Java code, there are useful methods in this utility class, here.

Is it possible to declare a global variable so that it can be used from every class in JSP?

I'm developing a little web application with JSP, this application requires a database connection, which i provide with a servlet.
The problem is, i would like that all the files on the server can use this class to access the db without creating each time a new connection, in fact in every file i would create a new Database object and a new connection would be established each time.
Is there a way to avoid this, maybe declaring a global object such as session and request, so that it will be initialized only one time and then used by all my JSP files?
Thanks
Why would you want to do this? Both solutions are the wrong way to go.
It's a bad idea because database connections are not thread safe. Creating a global object will degrade the throughput of your app, because every user will have to share the connection.
A better solution is to have a connection pool, maintained by the app server. Do a JNDI lookup to check a connection out of the pool, use it in the narrowest scope possible, and close the connection in that method scope.
Your app will scale better and won't risk thread safety.
You don't need a global variable, you need a functionality for your context, that will be used by your application. So you need to use JNDI, in Tomcat web page you have a how to do it, it is well explained and easy to implement. Best regards.
http://tomcat.apache.org/tomcat-7.0-doc/jndi-datasource-examples-howto.html
You seem to be talking about using directly a database connection from your JSP files, don't you? Well, this is not a theoretically correct solution because following separation of concerns principle, your view -your JSP file- shouldn't know anything at all about databases or data access. Check any description of MVC pattern, for instance this pretty obvious ;-):
Wikipedia on MVC pattern
But if you want to take the shorcut and have your view aware of your database you have, in my opinion, two options, both of them involving the JNDI lookup of a datasource previously defined in your appplication as Marcelo Tataje already said.
On how to define on Tomcat a JNDI datasource based on a connection pool:
http://tomcat.apache.org/tomcat-6.0-doc/jndi-datasource-examples-howto.html
In the same page you can see how to use this connection pool to issue queries right from your JSP (again very inadvisable in my opinion)
<%# taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<sql:query var="rs" dataSource="jdbc/TestDB">
select id, foo, bar from testdata
</sql:query>
<html>
<head>
<title>DB Test</title>
</head>
<body>
<h2>Results</h2>
<c:forEach var="row" items="${rs.rows}">
Foo ${row.foo}<br/>
Bar ${row.bar}<br/>
</c:forEach>
</body>
</html>
Is java.sql.Connection thread safe?
The other slightly better solution is, in your java code, JNDI lookup the datasource like this from your context listener:
public void contextInitialized(ServletContextEvent contextEvent) {
// JNDI Datasource lookup
InitialContext context = new InitialContext();
DataSource dataSource = (DataSource) context
.lookup("jdbc/DataSource");
// Storing datasource in application context
contextEvent.getServletContext().setAttribute("datasource",dataSource);
}
and after from any servlet use that stored datasource to get the database connection you need like this
((DataSource)contextEvent.getServletContext().getAttribute("datasource")).getConnection()
Don't use Connection class directly please. It's awfully primitive and obsolete. Nowadays nobody in a serious working environment handle connections like that. If you do what AmitG proposed you will end up with an unique connection shared by all concurrent accesses. See this on why this isn't the way to go

how to forward session from one servlet to another?

Hey guys i'm working on admin module for my project. When a person logs-in, a request is sent to login servlet. When it further ask for some other report by clicking other options a request for the report is sent to other servlet which gives the result on the page which is shown at the time of user which is of normal type. The session is lost between two servlets.
I am trying to navigate the generated report on some other page but for that i need to know user type in second servlet. This can be done by fetching value of user_type from login module bean class.
How to handle this situation? thanks
My login servlet is :
LoginService user = new LoginService();
user.setUserName(request.getParameter("username"));
user.setPassword(request.getParameter("password"));
user = UserDAO.login(user);
if (user.isValid())
{
HttpSession session = request.getSession(true);
session.setAttribute("currentSessionUser",user);
if(user.getUser_type().equalsIgnoreCase("admin")){
response.sendRedirect("administrator/homepage.jsp");
}else{
response.sendRedirect("homepage.jsp"); //logged-in page
}
}
else
response.sendRedirect("invalidlogin.jsp"); //error page
}
i tried using this in second servlet:-
LoginService session = (LoginService)request.getAttribute("currentSessionUser");
String drake = session.getUser_type();
System.out.println("usertype = " +drake);
Here LoginService is the bean class of login module. i'm get a nullpointer exception here.
I think you're trying to do stuff that your web container should handle for you... A session should automatically be maintained over the course of multiple servlet calls from the same client session. Methods from HttpServlet are given a HttpServletRequest. You can obtain the corresponding HttpSession using one of the getSession methods of that class.
You can bind stuff to the HttpSession using setAttribute and getAttribute.
EDIT: I'm taking this from the Servlet spec 2.5:
A servlet can bind an object attribute into an HttpSession implementation by name.
Any object bound into a session is available to any other servlet that belongs to the
same ServletContext and handles a request identified as being a part of the same
session.
I think you're better off getting the HttpSession object from the HttpServletRequest (at least assuming it's a HttpServlet) and setting/getting attributes through that. If you choose a proper name (it follows the same convention as Java package naming) for your attribute, you can be sure the returned object, as long as it's not null, can be cast to whatever type you put in there. Setting and getting attributes on the request itself isn't gonna help, I don't think stuff will get carried over from one servlet call to the next unless you call one servlet from the other with a RequestDispatcher, but that's not what you're after here.
So in your second code sample, do (LoginService)request.getSession().getAttribute("currentSessionUser");, that ought to work. Make sure to check for nulls and maybe choose an attribute name that uses your project's package name convention (like com.mycompany...).
I wouldn't mind a second opinion here since I'm not much of an EE/web developer.

How do I get a list of all HttpSession objects in a web application?

Let's say I have a running Java-based web application with 0 or more valid HttpSession objects associated with it. I want a way to access the current list of valid HttpSession objects. I was thinking that I could implement an HttpSessionListener and use it to append to a list of session id values that are stored in an application-scoped attribute, but then I'm on the hook to update the list as sessions are invalidated and who knows what else.
Before I start baking my own solution I thought I should ask the question:
Does the servlet API provide some means of getting access to the complete list of non-invalidated session objects?
I am using Tomcat 6.x as my web application container, and the MyFaces 1.2.x (JSF) library.
SOLUTION
I followed an approach similar to what BalusC discussed in these existing questions:
How to easily implement "who is
online" in Grails or Java Application
?
JSF: How to invalidate an
user session when he logs twice with
the same credentials
I modified by SessionData class to implement HttpSessionBindingListener. When a binding event happens, the object will either add or remove itself from the set of all the SessionData objects.
#Override
public void valueBound(HttpSessionBindingEvent event) {
// Get my custom application-scoped attribute
ApplicationData applicationData = getApplicationData();
// Get the set of all SessionData objects and add myself to it
Set<SessionData> activeSessions = applicationData.getActiveSessions();
if (!activeSessions.contains(this)) {
activeSessions.add(this);
}
}
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
HttpSession session = event.getSession();
ApplicationData applicationData = getApplicationData();
Set<SessionData> activeSessions = applicationData.getActiveSessions();
if (activeSessions.contains(this)) {
activeSessions.remove(this);
}
}
The one thing that continues to irritate me is what happens when Tomcat is restarted. Unless Tomcat has been properly configured to NOT serialize sessions to disk, it will do so. When Tomcat starts up again, the HttpSession objects (and the SessionData objects along with them) are deserialized and the sessions are made valid again. However, the serialization/deserialization totally sidesteps the HttpSession listener events, so I do not have the opportunity to gracefully put the deserialized reference to the SessionData back in my managed Set of objects after the restart.
I don't have any control over the production configuration of Tomcat in my customer's organization, so I cannot assume that it will be done the way I expect it.
My workaround is to compare the HttpSession creation time with the application startup time when a request is received. If the session was created before the application startup time, then I call invalidate() and the user is sent to an error/warning page with an explanation of what happened.
I get the application startup time by implementing a ServletContextListener and storing the current time inside an application-scoped object from within the contextInitialized() method of my listener.
No, the Servlet API doesn't provide a way. You really have to get hold of them all with help of a HttpSessionListener. You can find several examples in the following answers:
How to find HttpSession by jsessionid?
How to find number of active sessions per IP?
How to check Who's Online?
How to invalidate another session when a user is logged in twice?
There is no straight forward way. It depends on deployment. Above will fail once you decide to introduce distributed deployment and load balancing.
Not really an answer, but in the good ol' days there was "javax.servlet.http.HttpSessionContext", but it was dropped as of version 2.1, explicitly with no replacement: https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpSessionContext.html

Design question - Persistent data in a webapp session

I am developing a web app using servlets and jsps. I have a question about storing data I need to use across multiple servlets in a login session. When the user logs in, for example, I get the user object from the db and would like to store it somewhere and have the subsequent servlets and jsps use it without having to query the db again. I know that I have to store the object in a global array but am not able to figure out the best way to do this.
I am thinking of having a static hashmap or some other data structure created at webapp load time and I can use that to store the user object with the sessionID as the key for the hashmap.
Is there a better way? Any help is appreciated.
Thanks,
- Vas
You don't need to manage the sessions yourself. The servletcontainer will do it for you transparently in flavor of HttpSession. You normally use HttpSession#setAttribute() to store an object in the session scope and HttpSession#getAttribute() to get an object from the session scope. You can use HttpServletRequest#getSession() to get hold of a reference to the HttpSession.
E.g. in the login servlet:
User user = userDAO.find(username, password);
if (user != null) {
request.getSession().setAttribute("user", user);
} else {
// Show error?
}
You can get it back later in any servlet or filter in the same session by
User user = (User) request.getSession().getAttribute("user");
if (user != null) {
// User is logged in.
} else {
// User is not logged in!
}
You can even access it by EL in JSP:
<p>Welcome, ${user.username}!
(assuming that there's a Javabean getUsername() method)
There is a way to do this and it's defined in the servlet spec. You can get hold of the HttpSession object and add objects as "attributes".
Take a peek at the API here: http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/http/HttpSession.html
Depending on your needs and implementation, you can also consider following options:
making user object serializable and storing in session itself; in this case you must assure that subsequent changes to user object are propagated to the objected stored in session or DB (depending which will change)
storing only user ID in session and implement caching in your DAO/repository so no real DB query will be invoked if not necessary; if you are using Hibernate or some other ORM you might have this feature out of the box; this seems the least invasive as modifications on user object will be synchronized with application state and DB if properly handled by persistence layer
There are probably many more option out there.
We are constructing a social network like livemocha.com and we recommend you put the minimum possible in the session.
Storing only user ID in the session it's enough, and certainly, you don't need to assure that subsequent changes to the user object are propagated to the object stored in the session or DB (depending on which one will change). ;-)

Categories