I'm just starting out learning some JSP and servlets today and was wondering if it's possible to get the session's ServletContext as a variable and pass it to a plain Java class? If so, how may I do that?
My simple servlet:
public class myServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession(true);
//How do I receive the servlet context below in a plain Java class?
ServletContext sc = session.getServletContext();
request.setAttribute("sc", sc);
}
}
My Java class is just a plain one:
public class myClass extends HttpServlet {
//I want to be able to use the ServletContext as a variable that is passed from myServlet class into this one.
}
In myClass I want to be able to use it to get the real path file of a file within my project:
ServletContext sc
String path = sc.getRealPath(...)
EDIT: Can I do something like this in myServlet servlet?:
String realPath = sc.getRealPath("/WEB-INF/myFile");
But then how do I pass this realPath variable into myClass so I can use it there instead of in myServlet?
create a class
public class MyClass {....}
Have a variable of type ServletContext
private ServletContext myContext;
Set value through Constructor or setter
void setContext (ServletContext sc) {myContext = sc;}
Use it
myContext.get....("xxx");
Edit
You can use this class from your servlet as
doPost (....) {
....
ServletContext sc = session.getServletContext();
MyClass mc = new MyClass ();
mc.setContext (sc);
// now the context is **in** the MyClass instance - how you use it is up to you.
Related
In Java EE, we often use ActionContext,ServletContext and ServletActionContext, they have similar name, but I don't know the difference between them.
I only know the ServletActionContext inherit the ActionContext.
Someone can explain them?
There have many difference between them.
ServletContext
From the ServletContext's package(javax.servlet.ServletContext) we can know it is standard JavaEE WebApplication class library.
ServletContext provides the standard Servlet run-time environment. Actually is some methods of servlet communicate with web container.
public interface ServletContext {
// Returns the URL prefix for the ServletContext.
public String getServletContextName();
//Returns the context-path for the web-application.
public String getContextPath();
//Returns the ServletContext for the uri.
public ServletContext getContext(String uri);
//Returns the real file path for the given uri.
public String getRealPath(String uri);
public RequestDispatcher getNamedDispatcher(String servletName);
public RequestDispatcher getRequestDispatcher(String uri);
ServletContext is included in the ServletConfig, and ServletConfig often read from servlet or filter's init() method:
servletConfig.getServletContext()
filterConfig.getServletContext()
ActionContext
Comes from Struts, but at first comes from Struts1 and Struts2 they are different.
From Struts1:
A servlet (servlet org.apache.struts.action.ActionServlet) handle all the *.do action.
From Struts2:
The filter(org.apache.struts2.dispatcher.FilterDispatcher) handle all the request.
Because struts1 belongs to servlet scope. struts1 action's essentially is servlet.
struts2 action is normal Java bean, out of the servlet limitations.
The ActionContext makes up the lost WEB environment after the strtus2 action out of the stand servlet framework.
The ActionContext main function:
Provide the WEB context.
Solve the thread security issue.
Solve the incompatibility problem with other Framework(such as: webLogic))
ServletActionContext
As you say, ServletActionContext is ActionContext's subclass.
Its functions is starting at ActionContext, and encapsulate the methods, make it more simple and intuitive.
We can also study its source code:
public class ServletActionContext extends ActionContext implements StrutsStatics {
//HTTP servlet request
public static void setRequest(HttpServletRequest request) {
ActionContext.getContext().put(HTTP_REQUEST, request);
}
public static HttpServletRequest getRequest() {
return (HttpServletRequest) ActionContext.getContext().get(HTTP_REQUEST);
}
//HTTP servlet response
public static void setResponse(HttpServletResponse response) {
ActionContext.getContext().put(HTTP_RESPONSE, response);
}
public static HttpServletResponse getResponse() {
return (HttpServletResponse) ActionContext.getContext().get(HTTP_RESPONSE);
}
//servlet context.
public static ServletContext getServletContext() {
return (ServletContext) ActionContext.getContext().get(SERVLET_CONTEXT);
}
public static void setServletContext(ServletContext servletContext) {
ActionContext.getContext().put(SERVLET_CONTEXT, servletContext);
}
From above we can know the ServletActionContext extends ActionContext.
I read BalusC's excellent tutorial on JSF communication and it helped me establish the basics of my app. I would like to share the currently logged in User object that is set in the SessionScoped BaseBean class with all of its subclasses. Can this be done without injecting BaseBean as a #ManagedProperty for every single backing bean that needs to reference the logged in User?
My classes are listed below. Please let me know if more info is needed and I will be happy to update my question.
BaseBean Class
All other beans are subclasses of this bean. I am doing this to allow code reuse between beans.
#ManagedBean
#SessionScoped
public class BaseBean {
#EJB
protected UserDao userDao;
// Other DAOs along with methods (like isLoggedIn()) shared between beans
private User loggedInUser;
public User getLoggedInUser() {
return loggedInUser;
}
public void setLoggedInUser(User user) {
loggedInUser = user;
}
public boolean isLoggedIn() {
return loggedInUser != null;
}
}
LoginBean Class
Backing bean for the login page. To reduce number of DB calls I used the #ManagedProperty approach from the above tutorial to set the User object in the #SessionScoped BaseBean. Right now logging in and setting loggedInUser works as expected.
#ManagedBean
#RequestScoped
public class LoginBean extends BaseBean {
#ManagedProperty(value = "#{baseBean}")
protected BaseBean baseBean;
private String username;
private String password;
public String login() {
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.login(username, password);
} catch (Exception e) {
e.printStackTrace();
} finally {
baseBean.setLoggedInUser(userDao.getUser(username));
}
return "index";
}
public String getUserFirstName() {
return baseBean.getLoggedInUser().getFirstName();
}
// Getters and Setters, including for the #ManagedProperty baseBean.
}
CreateReport Class
This is an example of one backing bean from many. I want to reference the currently logged in User in order to create a report, however if the below code runs the User will be null! The only way I can get it to work is by adding a #ManagedProperty entry with getters and setters for BaseBean just like in LoginBean class. I would really like to avoid this as I will be copy-pasting this code to almost every single backing bean that I have!
#ManagedBean
#RequestScoped
public class CreateReport extends BaseBean {
private Report report = new Report();
public String createReport() {
report.setOwner(getLoggedInUser()); // Use inherited method
// instead of DI-ing BaseBean
reportDao.create(report);
return "index";
}
}
Used Software
Glassfish 4
Mojarra 2.2
Edit
One solution that I found is getting the instance of BaseBean from the FacesContext directly (I guess somehow the other beans are not in the same context or "don't see it?"). The following code (from BaseBean) would do what I want, however any bean subclass would have to invoke base() and that seems awkward and wrong.
protected FacesContext context = FacesContext.getCurrentInstance();
public BaseBean base() {
return (BaseBean) context.getApplication().evaluateExpressionGet(context, "#{baseBean}", BaseBean.class);
}
I can see you want to implement authentication and authentication controls, then you can use JSF Filters. You can keep your BaseBean class with session scope, create a new class which implements javax.servlet.Filter and in this class to get your BaseBean class through of session, for example:
public class LoginFilter implements javax.servlet.Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
BaseBean base = (BaseBean) req.getSession().getAttribute("baseBean");
if (base != null && base.isLoggedIn()) {
// to do something
chain.doFilter(request, response);
} else {
// to do something
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect(req.getContextPath() + "/index.xhtml");
}
}
public void init(FilterConfig config) throws ServletException {
// to do something
}
public void destroy() {
// to do something
}
}
Now, if you want to create a report with your BaseBean class, you can get your BaseBean class of session:
BaseBean base = (BaseBean) ( FacesContext.getCurrentInstance()
.getExternalContext().getRequest()).getSession().getAttribute("baseBean") );
Then my recommendation in your case is to avoid inheritance and use the advantages of JSF.
I hope this information helps you.
Good Luck.
making BaseBean a managed bean itself, and using it as a superclass for all other managed beans are two things that should not go along.
instead, you can:
remove #ManagedBean annotation from BaseBean.
save loggedInUser to session.
keep isLoggedIn as a protected method in BaseBean. you will be able to reach session via FacesContext there and get loggedInUser from session.
((HttpServletRequest)FacesContext.getCurrentInstance()
.getExternalContext().getRequest()).getSession()
ps: i don't know what the hell i was thinking when offering a static variable.
I have an independent project for writing test cases; the problem is I can't mock HttpServletRequest, simply because in my servlet there are calls like getServletContext() as test cases are running from the outside servlet container. It will always return an error saying "no context found". This is just one dependency with the servlet container; there can be hundreds. For example, initialContext.lookup() also depends on a container.
How can I use Mockito to write a test case in this scenario? Please don't ask for an error message; it's more of a logical problem than technical.
Looking on the internet for tutorials makes me wonder if I am doing something seriously wrong. No one seems to have encountered this problem before... How can you mock HttpServletRequest without ever having getServletContext() called in the servlet? I mean seriously, how rare can it be?
You have a servlet implementation that uses the ServletContext, e.g.
public class SomeServlet extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
Object attribute = servletContext.getAttribute("test");
System.out.println(attribute.toString());
}
}
in this case you have 2 options to test the doGet method
Use powermock's partitial mocking to only mock the getServletContext method.
#RunWith(PowerMockRunner.class)
public class SomeServletTest {
#Test
public void onGet() throws ServletException, IOException{
SomeServlet someServlet = PowerMock.createPartialMock(SomeServlet.class, "getServletContext");
ServletContext servletContext = PowerMock.createNiceMock(ServletContext.class);
HttpServletRequest httpServletRequest = PowerMock.createNiceMock(HttpServletRequest.class);
HttpServletResponse httpServletResponse = PowerMock.createNiceMock(HttpServletResponse.class);
someServlet.getServletContext();
PowerMock.expectLastCall().andReturn(servletContext);
servletContext.getAttribute("test");
PowerMock.expectLastCall().andReturn("hello");
PowerMock.replay(someServlet, servletContext, httpServletRequest, httpServletResponse);
someServlet.doGet(httpServletRequest, httpServletResponse);
}
}
or a simpler way is to just override the getServletContext method. In this case you don't need powermock. You can just do it using easymock. e.g.
public class SomeServletTest {
#Test
public void onGet() throws ServletException, IOException{
HttpServletRequest httpServletRequest = EasyMock.createNiceMock(HttpServletRequest.class);
HttpServletResponse httpServletResponse = EasyMock.createNiceMock(HttpServletResponse.class);
final ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class);
SomeServlet someServlet = new SomeServlet(){
public ServletContext getServletContext() {
return servletContext; // return the mock
}
};
EasyMock.expect(servletContext.getAttribute("test")).andReturn("hello");
EasyMock.replay(servletContext, httpServletRequest, httpServletResponse);
someServlet.doGet(httpServletRequest, httpServletResponse);
}
}
there can be 100's. like initialContext.lookup() also dependent on container.
In this case you can either create an InitialContext mock and use this.
If your code creates a new InitialContext, e.g.
public void someMethod(){
InitialContext context = new InitialContext();
context.lookup(....);
}
you can simply extract the InitialContext instantiation to a protected method that you can override in your tests like I showed above with the ServletContext
public void someMethod(){
InitialContext context = createInitialContext();
context.lookup(....);
}
protected InitialContext createInitialContext(){
return new InitialContext(); // can be overridden by a subclass
// and therefore by tests as well to
// return a mock
}
If you don't want or you can't modify the code in this way, then you can use Powermock to intercept the constructor.
EDIT
Can you post your Mockito code? It would be a pleasure, since the methods are named differently.
#Test
public void onGet() throws ServletException, IOException {
HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class);
HttpServletResponse httpServletResponse = Mockito.mock(HttpServletResponse.class);
final ServletContext servletContext = Mockito.mock(ServletContext.class);
SomeServlet someServlet = new SomeServlet(){
public ServletContext getServletContext() {
return servletContext; // return the mock
}
};
Mockito.doReturn("hello").when(servletContext).getAttribute("test");
someServlet.doGet(httpServletRequest, httpServletResponse);
}
Consider a simply servlet:
// MyServlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response)
{
UtilClass.doSomething(getServletContext().getRealPath(SOME_FILE));
}
And the utility class does something with the file:
// UtilClass.java
public String doSomething(String filePath)
{
File f = new File(filePath);
String s = readWhateverFrom(f);
return s;
}
I am now porting the doSomething() function to a web service running under Tomcat and Axis2. How would I port it so that I can still access the context and get access to a file under the servlet?
You should get ahold of your (jax-ws) MessageContext. This would depend on your configuration, but perhaps using
#Resource
private WebServiceContext wsCtx;
and in your method:
MessageContext messageContext = wsCtx.getMessageContext()
ServletContext ctx = (ServletContext)
messageContext.getProperty(MessageContext.SERVLET_CONTEXT);
Edit: Seems like Axis2 (as well as Axis) support the following:
HttpServlet servlet = (HttpServlet)
MessageContext.getCurrentContext().getProperty(HTTPConstants.MC_HTTP_SERVLET);
ServletContext ctx = servlet.getServletContext();
With the following imports:
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.http.HTTPConstants;
Sounds like a job for a Servlet Filter and a ThreadLocal. Axis is running within a Servlet Context, too. So all you have to do is to implement a custom javax.servlet.Filter, stuffing in the ServletRequest into a ThreadLocal where you can access it from within your utility class. You can get the ServletContext from the FilterConfig.
If we are coding a JSP file, we just need to use the embedded "application" object. But how to use it in a Servlet?
The application object in JSP is called the ServletContext object in a servlet. This is available by the inherited GenericServlet#getServletContext() method. You can call this anywhere in your servlet except of the init(ServletConfig) method.
public class YourServlet extends HttpServlet {
#Override
public void init() throws ServletException {
ServletContext ctx = getServletContext();
// ...
}
#Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
ServletContext ctx = getServletContext();
// ...
}
#Override
public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
ServletContext ctx = getServletContext();
// ...
}
}
See also Different ways to get Servlet Context.
The application object references javax.servlet.ServletContext and you should be able to reference that in your servlets.
To reference the ServletContext you will need to do the following:
// Get the ServletContext
ServletConfig config = getServletConfig();
ServletContext sc = config.getServletContext();
From then on you would use the sc object in the same way you would use the application object in your JSPs.
Try this:
ServletContext application = getServletConfig().getServletContext();
In a Java web application you often have the request object. So you can get the "application" object like this:
request.getServletContext().getServerInfo()