Do not hard code jsp links inside servlet - java

I am learning JSP and Servlets. Consider the following code inside the doPost method of a Servlet which forwards a HTTP request to a JSP -
RequestDispatcher view = request.getRequestDispatcher("/MyWebApp/MvcView.jsp");
I wonder what will happen if someone wants this servlet to forward the request to another jsp instead of the one above ? Does one have to change this code manually every time in their application ? How can one free oneself of all this manual work ?

One simple solution is to set the url as a parameter for your servlet:
<servlet>
<servlet-name>YourServlet</servlet-name>
<servlet-class>com.you.YourServlet</servlet-class>
<init-param>
<param-name>url</param-name>
<param-value>/MyWebApp/MvcView.jsp</param-value>
</init-param>
</servlet>
and the in the servlet:
public class YourServlet {
protected String url = null;
public void init(ServletConfig servletConfig) throws ServletException {
this.url = servletConfig.getInitParameter("url");
}
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
RequestDispatcher view = request.getRequestDispatcher(url);
}
}
then there is no need to recompile servlet code to chage the url.

Related

How to load servlet as first page in jsp application manage other servlets [duplicate]

This question already has answers here:
Change default homepage in root path to servlet with doGet
(2 answers)
Difference between / and /* in servlet mapping url pattern
(5 answers)
Closed 2 years ago.
I wanted to load a servlet as the first page in my simple jsp appication. Therefore I added the servlet in this URL mapping.
<servlet>
<servlet-name>StudentController</servlet-name>
<servlet-class>com.stu.controller.StudentController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>StudentController</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
And this is the Get method of above mentioned servlet.
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String path = request.getServletPath();
if (path.equals("/addstudent")) {
createStudent(request, response);
......
else {
searchAll(request, response);
}
}
protected void searchAll(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<StudentDTO> coList = null;
coList = StudentDAO.searchStudentList();
if (coList != null) {
request.setAttribute("stulist", coList);
} else {
request.setAttribute("msg", "No Record Found.");
}
RequestDispatcher rd = request.getRequestDispatcher("home.jsp");
rd.forward(request, response);
}
This is working as predicted, but the problem is I tried to create another servlet called CourseController and I mapped it in web.xml
<servlet>
<servlet-name>CourseController</servlet-name>
<servlet-class>com.stu.controller.CourseController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CourseController</servlet-name>
<url-pattern>/course/</url-pattern>
</servlet-mapping>
And the Get of the servlet is same as before.
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String path = request.getServletPath();
if (path.equals("/addcourse")) {
createCourse(request, response);
......
}
And I tried to access the servlet using an anchor tag in my home.jsp page.
Create Student
<br/>
Create Course
But I keep getting 404 error while trying to connect CourseController servlet.
This some how return to the searchAll method in StudentController servlet. I understand something is wrong with the mapping here. But I don't know what is that.
Please help.
Thank you.
You need to configure unique url-patterns, so "/" cannot work if you have more than one servlet.
Assuming that your first servlet has url-pattern "/newstudent" you can configure it as welcome-file:
<welcome-file-list>
<welcome-file>newstudent</welcome-file>
</welcome-file-list>

Framing multiple servlet request url pattern

I am using servlet to get request from frontend.
Am i able to make single servlet which could do multiple operation based on url pattern?
Here will be my url mapping
<servlet>
<servlet-name>Hello</servlet-name>
<servlet-class>HelloWorld</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Hello</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
<url-pattern>/HelloServletOne</url-pattern>
<url-pattern>/HelloServletTwo</url-pattern>
</servlet-mapping>
That means if i hit to the url as framed below it should invoke its own functionalities.
URL:/HelloServlet: it should do function 1
URL:/HelloServletOne: it should do function 2
URL:/HelloServletTwo: it should do function 3 etc.
How can i achive this by extending servlet.?
Code/link examples are much appreciated.
Regarding your url-pattern you need to know what URL was called. Because a request can be made due to different http-methods (GET, POST etc.) you can use parts of the FrontController Pattern
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloServlet extends HttpServlet {
private static final String SERLVET = "HelloServlet";
private static final String SERLVET_ONE = "HelloServletOne";
private static final String SERLVET_TWO = "HelloServletTwo";
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
processRequest(req, resp);
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
processRequest(req, resp);
}
private void processRequest(HttpServletRequest req, HttpServletResponse resp) {
String path = req.getServletPath();
switch (path) {
case SERLVET:
// ... call your function1
break;
case SERLVET_ONE:
// ... call your function2
break;
case SERLVET_TWO:
// ... call your function3
break;
default:
break;
}
// do something else
}
}
The getServletPath method may only work for explicit url-patterns like you have given. For other information on the URL check this link
You can handle multiple requests by the same servlet by making a contract to have a request parameter like 'ACTION'. Then in your forms add this as hidden field with values like 'ACTION_1' and 'ACTION_2' and 'ACTION_3'. So, in doPost() you can check this parameter value and can invoke respective handling methods in same servlet.
class YourServlet extends HttpServlet{
public void doPost(HttpReq req, HttpResp resp){
String action = reg.getParameter('ACTION');
if('ACTION_1'.equals(action)){
doAction_1();
}
if('ACTION_2'.equals(action)){
doAction_2()
}
if('ACTION_3'.equals(action)){
doAction_3()
}
else {
defaultAction();
}
}
}
I made into.
HttpServletRequest.getRequestURI() returns the URL pattern including /* with query parameter if exist, and HttpServletRequest.getPathInfo() returns the part matched by /* (or null for exact match).
Here in my case i need getPathInfo() where it will returns
HelloServlet,HelloServletOne or HelloServletTwo based on request.
Thanks.
You should not use three different Servlet for this purpose. You should use different methods of Servlet to achieve this.
Use doGet method for get data.
Use doPost method for insert data.
Use doPut method for update data.
Use doDelete method for delete data.
Please refer servlet api documentation for more details.
EDIT:
Read more about this here.
It says the url mapping you have provided must work if you are working with servlet api version 2.5 or greater.
Also, please make sure that you have provided fully qualified name of servlet class in <servlet-name>.

Default servlet with mapping and welcome file

I'm using Tomcat 7 to serve some JAXRS services.
I also want to get a few static web pages to be served by the same application, using default servlet. This is how I define the mapping :
public void contextInitialized(ServletContextEvent sce) {
sce.getServletContext().getServletRegistrations().get("default").addMapping("/backoffice/*");
}
My problem is that the only way to access those static files is to use http://myserver.com/backoffice/index.html. I would like to access them just with http://myserver.com/backoffice
I do not define any mapping in web.xml file, just my main JAXRS application.
I've tried using welcome file list this way :
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
I did not find any workaround on this problem, and the way I define the mapping to default servlet is the only one I found working.
Thanks for help.
I can only think of two possibilities.
Define a servlet mapping in the web.xml to the html file or
Create a servlet, annotate it with #WebServlet and then in the doGet() method dispatch/redirect to the html file.
You could dynamically register the servlet if you prefered.
What I ended with :
In my ServletContextListener, I added :
public void contextInitialized(ServletContextEvent sce) {
String name = "backoffice-filter";
sce.getServletContext().addFilter(name, new StaticRedirectionFilter(basePath, targetPath));
sce.getServletContext().getFilterRegistrations().get(name).addMappingForUrlPatterns(null, false, pathDepart);
sce.getServletContext().getServletRegistrations().get("default").addMapping("/backoffice/*");
}
The class StaticRedirectionFilter :
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
String requestURI = request.getServletPath();
if (requestURI.equals(basePath)) {
HttpServletResponse response = (HttpServletResponse) res;
response.sendRedirect(request.getContextPath() + targetPath);
}
else {
chain.doFilter(req, res);
}
}
As Alex mentionned it, I could have done it with an annotation #WebFilter("/backoffice") abose the StaticRedirectionFilter class, but using the mapping in context seems better for reusability.
I also think it works before Servlet 3, even if I didn't try it.

Best way to catch all unhandled exceptions in all Tomcat (GWT) servlets

I use Tomcat 7 and Lo4j for all my server logs and GWT for client (only AJAX calls).
All my unhandled exceptions get logged in my catalina.log.
Now I want to catch all exceptions and add some of the user's specific Tomcat SessionData.
There are several ways:
Try catch over all servlets (there must be a better solution).
http://tomcat.apache.org/tomcat-7.0-doc/aio.html: I would have to change my connecter, and I don't know if I could use the Tomcat Session within the Event Handler (EventType.ERROR).
Better way?
What would be the best way to achieve this?
Out of what I understood from your question, you can try to use at least one of two ways:
Basic Logging Servlet
If you have access to the source code of all of your servlets, you can make a little refactoring using a basic super-servlet that is responsible for the logging/whatever of every request working transparently with AJAX, no error forwards directives, and no global exception handlers. Suppose you use service(ServletRequest,ServletResponse) as the servlet entry point (but you can do the following for every do*() method either), then you can create an abstract super-servlet and simply inherit your servlets from it.
<servlet>
<servlet-name>servlet1</servlet-name>
<servlet-class>stackoverflow.Servlet1</servlet-class>
</servlet>
<servlet>
<servlet-name>servlet2</servlet-name>
<servlet-class>stackoverflow.Servlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet1</servlet-name>
<url-pattern>servlet1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servlet2</servlet-name>
<url-pattern>servlet2</url-pattern>
</servlet-mapping>
public abstract class BasicServlet extends HttpServlet {
/**
* Won't let it be {#code abstract} - we don't want to force any sub-servlet to implement this method.
*/
protected void doService(ServletRequest request, ServletResponse response) {
}
#Override
public final void service(ServletRequest request, ServletResponse response) {
try {
doService(request, response);
} catch ( Throwable ex ) {
err.println(ex.getMessage());
}
}
}
public final class Servlet1 extends BasicServlet {
#Override
protected void doService(ServletRequest request, ServletResponse response) {
out.println("I'm servlet #1");
}
}
public final class Servlet2 extends BasicServlet {
#Override
protected void doService(ServletRequest request, ServletResponse response) {
out.println("I'm servlet #2");
}
}
An advantage of this method is that you do not need to configure anything else than changing your servlet classes and not depend on the external configuration or context. The disadvantage is that you always must extend BasicServlet.
Filter
I didn't actually test it right now, for more information please see http://docs.oracle.com/javaee/6/api/javax/servlet/Filter.html . Filters allow to intercept each request (we use such a filter implementation for our JSPs while debugging and writing the exceptions into the common log file). The disadvantage is that it's not guaranteed that the filter can cover every exception/case, for example if any filter preceded your own filter.
<filter>
<filter-name>exceptionLoggingFilter</filter-name>
<filter-class>stackoverflow.ExceptionLoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>exceptionLoggingFilter</filter-name>
<url-pattern>*</url-pattern> <!-- we will process every request -->
</filter-mapping>
public final class ExceptionLoggingFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) {
try {
filterChain.doFilter(request, response);
} catch ( Throwable ex ) {
err.println(ex);
}
}
#Override
public void destroy() {
}
}
Hope this helps.
Just Overriding the GWT function doUnexpectedFailure worked.
#Override
protected void doUnexpectedFailure(Throwable t) {
ServerLog.error(t.getMessage(), t);
super.doUnexpectedFailure(t);
}
1) You can define error page for your webapp, like this:
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error</location>
</error-page>
Then you can bind another servlet at /error and handle the exception there.
2) You can setUncaughtExceptionHandler for every HTTP connector thread. You could use this technique with servlet filter which would contain reference to the current HttpRequest (say, via a thread local). This won't work with async I/O by the way.

How to handle exceptions thrown while rendering a view in Spring MVC?

I have a Spring MVC application which uses FreeMarker as View technology (But maybe the view technology doesn't really matter for my question). I need to intercept all exceptions which may get thrown during a request.
I have implemented a HandlerExceptionResolver but this resolver is only executed when the exception occurs within a controller. But when a controller returns a ModelAndView and the exception occurs while rendering the view (Because a variable was not found or something like this) then the exception resolver is not called and instead I get a stack trace in the browser window.
I also tried using an exception handler method within the controller which returns the view and annotated it with #ExceptionHandler but this also doesn't work (Most likely again because the exception is not thrown in the controller but in the view).
So is there some Spring mechanism where I can register an exception handler which captures view errors?
A word upfront: if you just need a "static" error page without much logic and model preparation, it should suffice to put a <error-page>-Tag in your web.xml (see below for an example).
Otherwise, there might be better ways to do this, but this works for us:
We use a servlet <filter> in the web.xml that catches all Exceptions and calls our custom ErrorHandler, the same we use inside the Spring HandlerExceptionResolver.
<filter>
<filter-name>errorHandlerFilter</filter-name>
<filter-class>org.example.filter.ErrorHandlerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>errorHandlerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The implementation looks essentially like this:
public class ErrorHandlerFilter implements Filter {
ErrorHandler errorHandler;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
try {
filterChain.doFilter(request, response);
} catch (Exception ex) {
// call ErrorHandler and dispatch to error jsp
String errorMessage = errorHandler.handle(request, response, ex);
request.setAttribute("errorMessage", errorMessage);
request.getRequestDispatcher("/WEB-INF/jsp/error/dispatch-error.jsp").forward(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
errorHandler = (ErrorHandler) WebApplicationContextUtils
.getRequiredWebApplicationContext(filterConfig.getServletContext())
.getBean("defaultErrorHandler");
}
// ...
}
I believe this should work pretty much the same for FreeMarker templates. Of course if your error view throws an error, you're more or less out of options.
To also catch errors like 404 and prepare the model for it, we use a filter that is mapped to the ERROR dispatcher:
<filter>
<filter-name>errorDispatcherFilter</filter-name>
<filter-class>org.example.filter.ErrorDispatcherFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>errorDispatcherFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/error/dispatch-error.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/WEB-INF/jsp/error/dispatch-error.jsp</location>
</error-page>
The doFilter-Implementation looks like this:
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
// handle code(s)
final int code = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (code == 404) {
final String uri = (String) request.getAttribute("javax.servlet.error.request_uri");
request.setAttribute("errorMessage", "The requested page '" + uri + "' could not be found.");
}
// notify chain
filterChain.doFilter(servletRequest, servletResponse);
}
You could extends the DispatcherServlet.
In your web.xml replace the generic DispatcherServlet for your own class.
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>com.controller.generic.DispatcherServletHandler</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Later create your own class DispatcherServletHandler and extends from DispatcherServlet:
public class DispatcherServletHandler extends DispatcherServlet {
private static final String ERROR = "error";
private static final String VIEW_ERROR_PAGE = "/WEB-INF/views/error/view-error.jsp";
#Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
try{
super.doService(request, response);
} catch(Exception ex) {
request.setAttribute(ERROR, ex);
request.getRequestDispatcher(VIEW_ERROR_PAGE).forward(request, response);
}
}
}
And in that page we only have to show a message to the user.
Not sure if my solution works with the problem you're having. Ill just post the way i catch my exceptions to ensure no stack trace is show inside the browser:
I made an AbstractController class with a method that will handle a specific conflict like this:
public class AbstractController {
#ResponseStatus(HttpStatus.CONFLICT)
#ExceptionHandler({OptimisticLockingFailureException.class})
#ResponseBody
public void handleConflict() {
//Do something extra if you want
}
}
This way whenever an exception occurs the user will see a default HTTPResponse status. (eg. 404 Not Found etc..)
I extend this class on all my controller classes to make sure errors are redirected to the AbstractController. This way I don't need to use ExceptionHandler on a specific controller but I can add the globally to all my controllers. (by extending the AbstractController class).
Edit:
After another go on your question, I noticed you're getting errors in your view. Not sure if this way will catch that error..
Hope this helps!!

Categories

Resources