I have two Java Servlets Organization1 and Organization2. I have saved the response value of Organization1 to one global variable called org1. Then I created a method getOrg1Name() in Organization1 which returns the value which is saved in that global variable org1. Please check code below:
public class Organization1 extends HttpServlet {
private String org1;
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter pw = response.getWriter();
response.setContentType("text/html");
this.org1 = request.getParameter("org1_name");
}
public String getOrg1Name()
{
return this.org1;
}
}
Then after I created a 2nd servlet Organization2. Inside the doPost() method of Organization2, I created a instance of Organization1 so that I can call that method getOrg1Name() which returns the value saved in global variable org1. Please check code below:
public class Organization2 extends HttpServlet {
private String org2;
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter pw = response.getWriter();
response.setContentType("text/html");
this.org2 = request.getParameter("org2_name");
Organization1 organization1 = new Organization1();
String org1 = organization1.getOrg1Name();
// org1 is always null. Why??
}
}
But each time method getOrg1Name() returns null. Can someone help me with this issue?
Field org1 in Organization1 is not a global variable in your case - it's a private field of class Organization1. That means when you create new instance of Organization1 field org1 is set to its default value. Default value of String is null.
If you want everything to work the way I see you want, you have to declare field org1 as static.
private static String org1;
In that case all instances of class Organization1 will have a link to one org1 instance.
However, this approach has a problem. Value of a field org1 will be rewritten with every request on servlet Organization1. So it is a good task to understand how static fields work, but code smell in real programming.
I am crawling through a servlet site and in almost every doPost I encounter code like this :
#Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// set encoding to UTF-8
if (request.getCharacterEncoding() == null)
request.setCharacterEncoding("UTF-8");
// TODO : this may be needed in doGet too ?????????
response.setCharacterEncoding("UTF-8");
// check if session exists
boolean sessionExists = request.isRequestedSessionIdValid();
HttpSession session = request.getSession();
if (!sessionExists)
session.setMaxInactiveInterval(1000);
// if session does not exist we create it
ServletContext context = session.getServletContext();
Integer numSessions = (Integer) context
.getAttribute("numberOfSessions");
if (numSessions == null)
context.setAttribute("numberOfSessions", 1);
else if (!sessionExists)
context.setAttribute("numberOfSessions", ++numSessions);
}
Would it be a good idea to create a BaseController class and move this code there - and should I move it in the init() method or in the doPost() - and then call super.doPost ? Also there are lines like session.setAttribute("photo", photo); in some of the servlets. Would it then be a good idea to have a session field in the BaseController - which if I understand things correctly should be volatile.
I am new to all this.
Thanks
You should use the Template Pattern.
Example:
public abstract class BaseController extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (request.getCharacterEncoding() == null) {
request.setCharacterEncoding("UTF-8");
}
response.setCharacterEncoding("UTF-8"); [...]
doService(request, response);
}
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception, Error;
}
And then you have to extend the BaseContoller class, and implement just the doService (or whatever) method.
You can move setting the request character encoding to a helper method and call it from doPost. Also check if your code behaved correctly in case that the encoding is set to something else than utf-8.
On the other hand, the session stuff is a bit weird. If you want to keep track of the number of sessions, remove all that and use javax.servlet.http.HttpSessionListener. It is more elegant and you will have your code in a single place.
If you need to keep track of variables that belong to a session, keep using the HttpSession class, do not save them in a field in the controller.
(I'm not sure exactly how to phrase the title here, and because of that I'm not really sure how to go about searching for the answer either.)
I have a Java servlet engine that handles requests. Say we have a doGet() request:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//set up user data
//do whatever the user requested
SomeClass c = new SomeClass();
c.doSomething();
}
Now in doSomething(), I want to be able to access which user made the request. Right now I'm doing it by creating a Java object within the method and passing it to wherever I need it:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//set up user data
MyUserObj userObj = new MyUserObj();
userObj.setId('123');
//do whatever the user requested
SomeClass c = new SomeClass(userObj);
c.doSomething();
}
By doing this, I have access to the instance of MyUserObj, and it can be further passed along in the application as needed.
I know in ASP.NET MVC3 I can acheive this by storing items/attributes for the current thread like this: HttpContext.Current.Items.Add("myId", "123"). HttpContext is then available in other functions without explicitly having to pass around an object.
Is there a way in Java to set some variables per request (or even set the MyUserObject to be accessed later) without passing the object through as a parameter?
There isn't in the servlet API, but you can make your own pretty easily. (Some frameworks like spring-mvc, struts provide such functionality)
Just use a public static ThreadLocal to store and retrieve the object. You can even store the HttpServletRequest itself in the threadlocal and use its setAttribute()/getAttribute() methods, or you can store a threadlocal Map, to be agnostic of the servlet API. An important note is that you should clean the threadlocal after the request (with a Filter, for example).
Also note that passing the object as parameter is considered a better practice, because you usually pass it from the web layer to a service layer, which should not be dependent on web-related object, like a HttpContext.
If you decide that it is fine to store them in a thread-local, rather than passing them around:
public class RequestContext {
private static ThreadLocal<Map<Object, Object>> attributes = new ThreadLocal<>();
public static void initialize() {
attributes.set(new HashMap<Map<Object, Object>>());
}
public static void cleanup() {
attributes.set(null);
}
public static <T> T getAttribute(Object key) {
return (T) attributes.get().get(key);
}
public static void setAttribute(Object key, Object value) {
attributes.get().put(key, value);
}
}
And a necessary filter:
#WebFilter(urlPatterns="/")
public class RequestContextFilter implements Filter {
public void doFilter(..) {
RequestContext.initialize();
try {
chain.doFilter(request, response);
} finally {
RequestContext.cleanup();
}
}
}
You can attach an object to the current request with setAttribute. This API is primarily used for internal routing, but it's safe to use for your own purposes too, as long as you use a proper namespace for your attribute names.
I'm working on a homework project where we implement sessions (can't use HTTPSession) using cookies and storing session data. We're supposed to store in memory and not an external database.
To keep things global to the instance and threadsafe, I have a singleton class SessionTable which contains a synchronized LinkedHashMap of SessionData objects.
SessionTable
public class SessionTable implements Serializable{
private static final long serialVersionUID = 3563658006793791512L;
private Map<String, SessionData> table;
private SessionTable(){
this.table = Collections.synchronizedMap(new ExpiryLinkedHashMap<String, SessionData>());
}
private static class SessionTableHolder {
public static final SessionTable instance = new SessionTable();
}
public static SessionTable getInstance() {
return SessionTableHolder.instance;
}
}
SessionSaver Servlet
public class SessionSaver extends HttpServlet {
public SessionSaver() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
SessionTable sessionTable = (SessionTable) getServletContext().getAttribute("table");
[...]
}
}
ServletListener
Based on another question here at SO I tried this method of using a listener (instead of adding the table in the constructor of the Servlet, which didn't work). I added it in my web.xml too.
#WebListener
public class ServletListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent arg0) {
arg0.getServletContext().setAttribute("table", SessionTable.getInstance());
}
}
Basically, I think I have my logic working correctly to store all the values I need in cookies, but none of this is getting persisted in the table, so when a new request comes in it thinks its a new user and always gives back a brand new cookie. What am I doing wrong? How can I get save this between requests?
I don't see any code saving some values in your table, however the entire code looks fine (I mean that there should be one instance of table). Does your client resend previously received cookies (Cookie header)? Otherwise, it would be treated as a new client, sending request for the very first time.
I have the following two classes and I am starting to see a pattern that even with my little Java background is screaming for a fix. Every new Object is going to require a set of Actions and the number of classes could grow out of hand. How do I refactor this into a generic DeleteAction class?
I know some of the answers will be use Hibernate, or JPA, or some Framework, but at the moment I can't utilize any of those tools. Oh, and our server only has jdk 1.4 (don't ask!). Thanks.
public class DeleteCommitmentAction implements ControllerAction {
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException {
CommitmentListDAO clDAO = new CommitmentListDAO();
CommitmentItemForm ciForm = new CommitmentItemForm(clDAO);
CommitmentItem commitmentItem = ciForm.deleteCommitmentItem(request);
RequestDispatcher view = request.getRequestDispatcher("views/commitmentView_v.jsp");
view.forward(request, response);
}
}
.
public class DeleteProgramAction implements ControllerAction {
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException {
ProgramDAO prgDAO = new ProgramDAO();
ProgramForm prgForm = new ProgramForm(prgDAO);
ProgramForm prg = prgForm.deleteProgram(request);
RequestDispatcher view = request.getRequestDispatcher("views/programView_v.jsp");
view.forward(request, response);
}
}
The approach that I think I need to take is to make interfaces. Starting with the DAO, I have created the following interface.
public interface GenericDao {
public void create(Object object, STKUser authenticatedUser) throws DAOException;
public void retreive(String id, STKUser authenticatedUser) throws DAOException;
public void update( final Object object, STKUser authenticatedUser) throws DAOException;
public void delete(String id, STKUser authenticatedUser) throws DAOException;
}
And then in my DeleteAction class I tried this
GenericDao gDAO = new GenericDao();
but Eclipse is stating "Cannot instantiate the type GenericDao" So now I am lost.
Update: Based on Péter Török's answer, here is what I have:
This is the servlet specific for handling operations on Commitment Items:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String schema = General_IO.getSchemaPath("TPQOT_463_COMMITMENT", request.getServerName());
CommitmentListDAO clDAO = new CommitmentListDAO();
CommitmentItemForm ciForm = new CommitmentItemForm(clDAO);
CommitmentItem commitmentItem = new CommitmentItem();
// I think this is the Application Controller Strategy
actionMap.put(null, new ListCommitmentsAction());
actionMap.put("list", new ListCommitmentsAction());
actionMap.put("view", new ViewCommitmentItemAction(schema));
//actionMap.put("delete", new DeleteCommitmentAction(schema));
// Change to the Generic DeleteAction and pass in the parameters
actionMap.put("delete", new DeleteAction(ciForm, commitmentItem, schema, "views/commitmentDeleteConfirm_v.jsp", "views/commitmentView_v.jsp" ));
// When happy with this approach, change other actions to the Generic Versions.
actionMap.put("sqlConfirmDelete", new DeleteCommitmentConfirmAction());
actionMap.put("edit", new EditCommitmentItemAction(schema));
actionMap.put("sqlUpdate", new UpdateCommitmentItemAction1(schema));
actionMap.put("new", new NewCommitmentFormAction(schema));
actionMap.put("sqlInsert", new InsertCommitmentItemAction1(schema));
String op = request.getParameter("method");
ControllerAction action = (ControllerAction) actionMap.get(op);
if (action != null) {
action.service(request, response);
} else {
String url = "views/errorMessage_v.jsp";
String errMessage = "Operation '" + op + "' not a valid for in '" + request.getServletPath() + "' !!";
request.setAttribute("message", errMessage);
request.getRequestDispatcher(url).forward(request, response);
}
}
And here is the Generic DeleteAction:
public class DeleteAction implements ControllerAction {
private Form form;
private Object obj;
private String schema = null;
private String xPage;
private String yPage;
public DeleteAction(Form form, Object item, String schema, String yPage, String xPage) {
this.form = form;
this.item = item; //passed in javabean??
this.schema = schema;
this.xPage = xPage;
this.yPage = yPage;
}
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
item = form.delete(request);
/* Database schema is described in xml files.
Hash maps of field names, sizes, and titles; foreign key names, titles,
lookup tables; and primary keys information are used to dynamically
build HTML forms in the views.
*/
HashMap test = ReadTableSchema.returnSchema(schema);
HashMap hshFields = (HashMap) test.get("hshFields");
HashMap hshForeignKeys = (HashMap) test.get("hshForeignKeys");
HashMap hshPrimaryKeys = (HashMap) test.get("hshPrimaryKeys");
request.setAttribute("hshFields", hshFields);
request.setAttribute("hshPrimaryKeys", hshPrimaryKeys);
request.setAttribute("hshForeignKeys", hshForeignKeys);
request.setAttribute("item", item);
request.setAttribute("form", form);
request.setAttribute("pageName", "Delete");
//Check for deletion authorization if successful forward to the confirmation page
if (form.isSucces()) {
request.setAttribute("message", "Please confirm permanent deletion of the data below.");
RequestDispatcher view = request.getRequestDispatcher(yPage);
view.forward(request, response);
} else {
// Not authorized to delete the data so just re-display
RequestDispatcher view = request.getRequestDispatcher(xPage);
view.forward(request, response);
}
}
}
then here is the interface (right now just for delete) that will be used by all forms.
public interface CRUD {
public Object delete(HttpServletRequest request);
}
You can't instantiate an interface, you need a concrete subclass for that. However, creating concrete subclasses just increases the number of classes, which you are trying to avoid. It is better to use composition instead of inheritance.
Namely, if you manage to make a common interface for the forms, and hide the actions deleteCommitmentItem, deleteProgram etc. behind one single method, you can parametrize your action instances with the required form (or a factory to provide this), e.g.:
public class GenericAction implements ControllerAction {
private Form form;
private String page;
GenericAction(Form form, String page) {
this.form = form;
this.page = page;
}
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException {
Item item = form.performDelete(request);
RequestDispatcher view = request.getRequestDispatcher(page);
view.forward(request, response);
}
}
...
CommitmentListDAO clDAO = new CommitmentListDAO();
CommitmentItemForm ciForm = new CommitmentItemForm(clDAO);
GenericAction deleteCommitmentAction = new GenericAction(ciForm, "views/commitmentView_v.jsp");
ProgramDAO prgDAO = new ProgramDAO();
ProgramForm prgForm = new ProgramForm(prgDAO);
GenericAction deleteProgramAction = new GenericAction(prgForm, "views/programView_v.jsp");
Thus you need no new classes for new kinds of actions, just instantiate GenericAction with different parameters.
It's clear by your naming that you already have implemented DAO objects (CommitmentListDAO, ProgramDAO). You should (probably) modify these classes to implement your new interface. Then your problem now becomes, how do you know which DAO to instantiate when you're in your generic delete action. Either that DAO should be passed into your action directly, or some other information on how to instantiate it (either a Class or factory) must be provided to your action.
GenericDAO is an interface, it cannot be instantiated directly. I don't know much Java, but every OOP language is pretty much the same. So what you need to do is create a concrete implementation of your interface (as a class) and then instantiate that instead. Something like this (sorry for the C# code but you get the idea):
public interface IGenericDAO {
void create(...);
}
and the implementation:
public class GenericDAO implements IGenericDAO {
public void create(...) {
/* implementation code */
}
}
Does that make sense?
One servlet per action is not unreasonable. Consider that if you have to do some action X, then you need to do X. Write a servlet to do X. It's that simple.
As you're noticing, this could lead to a lot of nearly identical servlets. That's ok because now you can use delegation (as Peter Torok recommends) or inheritance to move all the shared and abstracted code into one place. Which is better? Either is better than neither. You are 90% of the way to victory if you use one or both as appropriate.
I prefer a main servlet from which all others inherit. This allows me to wrap every service call in a consistent proper transaction in the base controller class. The subclasses never have to worry about it. This code shows the gist of it.
public class BaseControllerAction implements ControllerAction {
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException {
Connection conn = null;
try {
conn = getAConnection();
log.info("+++ top of "+getClass().getName());
conn.getTranaction().begin();
String dest = go(request, response, conn);
conn.getTransaction().commit();
RequestDispatcher view = request.getRequestDispatcher(dest);
view.forward(request, response);
} catch (Exception e) {
conn.getTransaction().rollback();
} finally {
conn.close();
log.info("--- Bottom of "+getClass().getName());
}
protected abstract String go(HttpServletRequest request, HttpServletResponse response, Transaction transaction) throws ServletException;
}
and now you can implement your servlet:
public class DeleteCommitmentAction extends BaseControllerAction {
protected String go(HttpServletRequest request, HttpServletResponse response, Connection conn) throws ServletException {
// Given what this method is supposed to do, it's very reasonable
// to refer to models and DAOs related to deleting commitments.
Long id = new Long(request.getParameter("id"));
CommitmentDAO.delete(conn, id);
return "views/commitmentView_v.jsp";
}
}
So now none of your servlets have to worry about transactions or opening and closing connections. They only have to worry about the details of their specific task. Obviously I don't know your system so I can't give detailed suggestions but this is how I did two decent-sized apps recently. They have about 30 servlets each. But the servlets are generally about 15 lines long. I ended up with a utility class that implemented the sorts of tasks needed by all the servlets. Poor man's delegation, perhaps.
Interface can't be instantiated. Instead, you should create a concrete class implementing the interface and instantiate this class.