Could anybody explain me what is the difference between this ways of getting the ServletContext of an HttpServlet?
doGet( HttpServletRequest request, ... ){
getServletConfig( ).getServletContext( );
request.getSession( ).getServletContext( );
getServletContext( );
}
Is there any difference in performance or in the context itself? If so, which is the best way? Are there any other way of retrieving the context?
There's one more.
request.getServletContext();
There's technically no difference in performance, only the request.getSession() will implicitly create the HTTP session object if not created yet. So if this is not done yet, then grabbing the servlet context via the session may take a few nanoseconds longer if the session isn't created yet.
There's also no difference in the returned context. Those methods are all just for convenience and which method to obtain the context depends on the context ;) you're currently sitting in.
If you're sitting in a method invoked by servlet's service() (such as doGet(), doPost(), etc), then just use the inherited getServletContext() method. Other ways only unnecessarily add more characters to the source code.
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
ServletContext context = getServletContext();
// ...
}
If you're sitting in servlet's init(ServletConfig) method, then the inherited getServletContext() isn't available yet as long as you haven't called super.init(config). You'd need to grab it from ServletConfig.
#Override
public void init(ServletConfig config) {
ServletContext context = config.getServletContext();
// ...
}
But much better is to override init() instead. In a decent servlet you usually never need to override init(ServletConfig).
#Override
public void init() {
ServletContext context = getServletContext();
// ...
}
If you're not sitting in a servlet but in e.g. a filter which lacks the inherited getServletContext() method and you only have ServletRequest at hands, then you could grab it from there.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
ServletContext context = request.getServletContext();
// ...
}
Note that this is new since Servlet 3.0. Previously, you'd have to grab it from the session.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
ServletContext context = request.getSession().getServletContext();
// ...
}
This is however not nice if you worry about unnecessary session creation. Hence the introduction of ServletRequest#getServletContext() — although you could also simply extract it from FilterConfig (hey, there's yet another way!).
private FilterConfig config;
#Override
public void init(FilterConfig config) {
this.config = config;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
ServletContext context = config.getServletContext();
// ...
}
And then there are HTTP session listeners where you could listen on a.o. session destroy. There's no other way to obtain the servlet context than via HttpSession#getServletContext().
#Override
public void sessionDestroyed(HttpSessionEvent event) {
ServletContext context = event.getSession().getServletContext();
// ...
}
Here you don't need to worry about unnecessary session creation because it's at that point already created for long beforehand. Do note that there's no ServletRequest anywhere as there's not necessarily means of an active HTTP request during server side session timeout.
As last, there's also ServletContext#getContext() which returns the ServletContext of a different web application deployed to same server (this works only if the server is configured to enable cross context access on the target webapp).
ServletContext otherContext = context.getContext("/otherContextPath");
But this already requires the current ServletContext to start with, for which you should by now already know which way to use to obtain it.
Related
I am developing a JAX-RS multi-domain application. This application is supposed to be addressed by different domain names and shall handle these domains differently. Each domain is a record in DB and maps the domain name to UUID. Records of all domains are set to the same server IP. UUIDs are used internally as a path parameter (for example: https://{IP}/{uuid}).
I would like to achieve the following state:
https://domain1.com/{someResource} --> https://{serverIP}/domain/123e4567-e89b-12d3-a456-426655440000/{someResouce}
The illustration case:
The user accesses the resource on the URL https://my-domain.com/rest/v1/details and the server serves the details about the current domain (and similarly in other requests).
My idea was to implement a ContainerRequestFilter which would add information about requested URL (and hostname) like it works in authentication filters. The second approach was to use the Ocpsoft Rewrite library but I failed in setting it up and the documentation is not very rich in this topic.
Hi achieved it by using javax.servlet.Filter
Check it out:
#ApplicationScoped
#WebFilter(filterName = "MyAwesomeFilter", urlPatterns = {"/*"})
public class MyAwesomeFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
final String s = request.getRequestURI() + "/addedSomething"
request.getRequestDispatcher(s).forward(request, servletResponse);
}
#Override
public void destroy() {
}
}
I'm trying to see if there's a way to pre-check all the requests before they are routed to the correct method?
all of my requests would have version# in param. it would be really helpful to check that at first place and decide whether the client needs to upgrade or not. it is a bit painful to check in all the methods, so I was wondering if there's a way to 'filter' the request at first. Thanks!
What you want is called a Filter.
See The Essentials of Filters
Example
#WebFilter( urlPatterns = "/*" ) // Filter all URLs
public class VersionFilter implements Filter {
#Override
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException {
// TODO Do your filtering here
}
#Override
public void init( FilterConfig filterConfig ) throws ServletException { ... }
#Override
public void destroy() { ... }
}
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);
}
i want to ask you about mvc. How it works. So, this is simple example(I don't use any frameworks)
in Controller(Servlet):
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
private void processRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String page = null;
AbstractCommand action;
action = ActionFactory.getAction(request);// get command from factory
page = action.execute(request, response);
RequestDispatcher dispatcher = getServletContext()
.getRequestDispatcher(page);
dispatcher.forward(request, response);
}
for action we create a common interface(Strategy pattern):
public interface AbstractAction {
public String execute(HttpServletRequest request, HttpServletResponse response);
}
Simple Action(Example):
public class HelloAction implements AbstractAction {
#Override
public String execute(HttpServletRequest request,
HttpServletResponse response) {
//....(some actions to modify request)
String page = "/main.jsp";
return page;
}
}
And now, our factory:
public class ActionFactory {
private enum Actions{
HELLO;
}
public static AbstractAction getAction(HttpServletRequest request){
String action = request.getParameter("action");//take parameter from jsp
Actions actionEnum = Actions.valueOf(action.toUpperCase());
switch (actionEnum) {
case HELLO:
return new HelloAction();
}
}
}
We came to the place where I am in confused. Servlet is initialized only once, and only one for all requests. Just forwards requests to the actions where we modify request or response. But, we create NEW instance of the class for every request. Here can occur memory overflow!? Or not?
Can we make these actions static(static method, one for all request)? If two requests come at the same time what will happen to them?
What do you think about this, please share your experience.
P.S. sorry for bad english.
How about Singleton pattern to get the instance of the Action class ?
Just add some abstact getInstance() method in AbstractAction.
Make every implementation provide its own instance.
In every implementation class, use Singleton pattern so that only one instance exists.
Make sure no action class stores any data related to a specific request.
As i understood the jsp, the whole thing is stateless, if u access the servlet by http request, the servlet will be created in a new instance.
After leaving the servlet by .forward(), it will be released by garbage collection.
2,3,...,n requests = 2,3,...,n servlets.
by forwarding to a jsp, the only way to access the servlet from jsp is a new http request = new servlet. ( will move to the doPost method)
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()