Suppose I have a servlet that does all necessary processing in a method defined like this:
protected abstract void process(ServletRequest request, ServletResponse response);
What is the correct way to implement Servlet interface?
Wrap runtime exceptions in ServletException:
protected void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
try {
process(request, response);
} catch(Throwable t){
throw new ServletException(t);
}
}
or throw runtime exceptions as is:
protected void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
process(request, response);
}
Wrap your code only if you have to handle the exception, otherwise, wrap is not required.
Related
I have a Jakarta based app, that has a number of servlets and a common filter that sits in front of them all.
Some of the servlets may throw an exception and I'd like to handle it inside the filter using a try .. catch
But when an exception is thrown, I see the stack-trace pasted below even though I'm catching inside the filter.
Is there a way to gracefully handle the exception in this manner, preventing it from being logged?
MyFilter.java:
public class MyFilter implements Filter {
#Override
public void doFilter(
ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
try {
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
try {
filterChain.doFilter(servletRequest, servletResponse);
} catch (RuntimeException e) {
httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
httpServletResponse.getWriter().write("error");
httpServletResponse.getWriter().flush();
}
}
}
}
MyServlet.java:
public class MyServlet extends HttpServlet {
#Override
protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) {
throw new RuntimeException("Oops");
}
#Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) {
throw new RuntimeException("Oops");
}
}
Causes this exception to be logged:
Jun 22, 2022 9:28:16 P.M. org.apache.catalina.core.ApplicationDispatcher invoke
SEVERE: Servlet.service() for servlet [MyServlet] threw exception
java.lang.RuntimeException: Ooops
at com.MyServlet.doPost(MyServlet.java:63)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
There are two types of exception in Java: checked exception (for e.g. ClassNotFoundException, IOException, FileNotFoundException) and unchecked exception (which is also known as runtime exception). RuntimeException belongs to unchecked exception. If you want to handle the Exception in try..catch, you might want to use/throw checked exception.
UPDATED:
Have you tried this? Unfortunately I couldn't test it
…
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
throw new ServletException("Oops");
}
...
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
try {
filterChain.doFilter(request, response);
} catch (ServletException e) {
//handle the Exception gracefully
}
}
I want to add permissions to servlet methods in declarative way, for example:
// servlet
#Perms("admin", "finance")
public void doPost(servletRequest req, servletResponse res) {
...
}
// web filter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
List<String> allowedRoles = ... // somehow get values from #Perms
}
May be there is some other ways to do it without annotations, it is just example of idea what I want to do.
Or more abstract example:
#WebServlet("/someaddress")
// servlet
#What("have a nice day")
public void doPost(servletRequest req, servletResponse res) {
...
}
#WebFilter("/*")
// web filter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
String msg = ... // somehow get values from #What
}
Here is solution
declare annotation
#Retention(RetentionPolicy.RUNTIME)
public #interface What {
String[] value();
}
override init method in servlet (I think it will be usefull to declare another class that all servlet will be inherit with that init)
#Override
public void init() throws ServletException {
ServletContext ctx = this.getServletContext();
final Class[] sFormalArgs = {HttpServletRequest.class,HttpServletResponse.class};
try {
Method m = this.getClass().getDeclaredMethod("doGet", sFormalArgs); // do the same with other methods
What a = m.getAnnotation(What.class);
String[] value = a.value();
ctx.setAttribute("someStuff", value);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
add annotation to method
#What({"admin", "finance"})
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
{
....
}
get it in web filter
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain
) {
HttpServletRequest req = (HttpServletRequest) servletRequest;
ServletContext ctx = req.getServletContext();
Object o = ctx.getAttribute("someStuff");
}
do not forget to handle errors and multithreding issues
I have a two servlets.
In my first servlet I use sendRedirect construction, but it call doGet from second servlet:
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (someCondition()) {
resp.sendRedirect(req.getContextPath() + "/urlPattern");
} else { ... }
}
And my second servlet:
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// something to do...
}
But this is not security, user may get second servlet through compose URL. This is not permissible for my case, I need replace call sendRedirect to directly use doPost in second servlet.
Please help me replace resp.sendRedirect(...) to something calling doPost. Thank You.
I was working on a web project using java servlet and jsp pages. In one of the servlet we have RequestDispatcher method and which is calling another servlet.
#WebServlet("/Demo")
public class DemoServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.sendRedirect("testing"); //calling other servlet
}
}
#WebServlet("/testing")
public class TestingServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("Hello World");
}
}
So, now I wanted to prevent contextRoot/testing from being invoked directly from the browser but instead only let it invoked from the other servlet(Demo)
Please suggest me if there is any way to do that.
Couple of techniques exist:
Look at writing a HTTP Request Filter. You can then inspect the incoming request and the url and reject it if the pattern matches the servlet paths that you do not want to be invoked directly.
Another mechanism is to use the security constraints in your web.xml to allow access to various paths in your application only to authorized users/roles. Look at <security-constraint> tag in web.xml
Answer given by "Romin" is correct. You have to use Filters for this. what you can do is, you can set a new session variable whenever "/Demo" url is accessed and in the filter check for the condition that session exists, if it exists allow the url or else throw error. You could do something similar like this. In "Demo" servlet
#WebServlet("/Demo")
public class DemoServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
HttpSession session = request.getSession() //get new session
res.sendRedirect("testing"); //calling other servlet
}
}
In Filter class add the below code
#WebFilter("/login")
public class MyFilter implements Filter{
public void init(FilterConfig arg0) throws ServletException {}
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpRequest request = (HttpRequest) req;
HttpResponse respone = (HttpResponse) res;
HttpSession session = request.getSession(false) //get the existing session object
if(null != session) {
chain.doFilter(req, resp);
} else {
"redirect to some error page or home page"
}
}
public void destroy() {}
}
One approach is to check the caller's ip using ServletRequest.getRemoteAddr() and rejects it if it's not called locally
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
if(!req.getRemoteAddr().equals("127.0.0.1")) { // reject }
}
However this method wouldn't work legitimate caller (eg: proxy) is also using the same ip.
My servet work fine for get requests but when I call POST (using jquery ajax $.post) I get error 405 (Method Not Allowed)
Here is my code:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class init extends HttpServlet {
public init() { }
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.println("GET");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, IllegalStateException {
response.setContentType("application/json");
ServletInputStream in = request.getInputStream();
PrintWriter out = response.getWriter();
out.print("POST");
}
}
This happened to me when my method call
"super.doGet(req, resp)" or "super.doPost(req, resp)" .
After i removed above super class calling from the doGet and doPost it worked fine.
Infact those super class calling codes were inserted by the Eclipse IDE template.
Are you sure that you actually override the doPost Method?
The signature looks like this:
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException
but you specify it like this:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, IllegalStateException
I am not sure whether this is allowed:
Overriding Java interfaces with new checked exceptions
Try the following steps:
Add the #Override Annotation
Remove the throws IllegalStateException (shouldn't be a problem as it is a runtime exception)