Access servlet init parameters from filter - initialization

I have a servlet like this:
#WebServlet("/a/path")
#WebInitParam(name="name", value="name_value")
public class MyServlet extends HttpServlet {
//...
On this servlet I have put a filter:
#WebFilter(dispatcherTypes = { DispatcherType.REQUEST }, urlPatterns = { "/a/*" })
public class MyFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//...
HttpServletRequest req = (HttpServletRequest)request;
//problem comes here
System.out.println(req.getServletContext().getInitParameter("name"));
//...
}
The problem is, that even if I set the #WebInitParameter in MyServlet, the programs prints out a null string (see the commented line //problem comes here in MyFilter). I verified and saw that init() method from servlet is executed before of doFilter().
So can anyone light me on this issue? Why the initParameter "name" is null, if it is set up to a value?
Thanks!

I think WebInitParam is defining init parameters for servlet and not for whole application context, so if you want acces parameters through ServletContext object, then define context params in you web.xml deployment descriptor.

Related

Get Cookie after its set in doFilter

I have a CookieFilter class that overrides doFilter method to set a Cookie before my Rest service is invoked:
import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;
public class CookieFilter implements Filter {
#Override
public void init(FilterConfig config) throws ServletException {}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (notPresent("TEST")) {
String uuid = UUID.randomUUID().toString();
httpResponse.addCookie(new Cookie("TEST", uuid));
}
chain.doFilter(request, response);
}
#Override
public void destroy() {}
private boolean notPresent(String cookieName) {
// here are the checks
}
}
Rest service method:
void myRestServiceMethod(#Context HttpServletRequest request) {
Cookie[] cookies = request.getCookies(); // has my cookie inside after second call
// other logic bellow
}
myRestServiceMethod is called after doFilter but Cookie is not present.
However, I am able to read the cookie (using JAX-RS #Context to retrieve HttpServletRequest object) in second client call to myRestServiceMethod where Cookie (set in a first call) is sent from the client and passed to the server.
My question is: is there a way read the Cookie in a first call to myRestServiceMethod after its set in doFilter?
is there a way read the Cookie in a first call to myRestServiceMethod after its set in doFilter?
No.
There are 2 solutions:
Refresh the request after adding cookie.
if (notPresent("TEST")) {
String uuid = UUID.randomUUID().toString();
httpResponse.addCookie(new Cookie("TEST", uuid));
httpRequest.sendRedirect(httpRequest.getRequestURI()); // NOTE: you might want to add query string if necessary.
}
else {
chain.doFilter(request, response);
}
Or, better, store it as request attribute.
String uuid = getCookieValue("TEST");
if (uuid == null) {
uuid = UUID.randomUUID().toString();
httpResponse.addCookie(new Cookie("TEST", uuid));
}
request.setAttribute("TEST", uuid);
chain.doFilter(request, response);
So that you can simply do this.
String uuid = (String) request.getAttribute("TEST");
If CDI is available in the environment, you could populate a #RequestScoped bean instead.
That said, it's strange to have a JAX-RS service to (indirectly) deal with cookies. REST is never intented to be stateful.

Class level request mapping #requestmapping execute some code on every request to that class

#Controller
#RequestMapping(value = {"user"})
public class UserController {
...
#RequestMapping(value = {"dashboard"})
public String index(HttpServletRequest req, ModelMap map) {
this.objSession = req.getSession(false);
try {
System.out.println(this.objSession.getAttribute("userid"));
I am using Spring 4.2.
Suppose I have this class and I want to check the session object having the attribute userid=1 or not.
I am doing this checking in every methods under this "/user" request.
My query is that if I can avoid this same coding which i am doing before executing any codes of any methods.
Is there any way round to increase code resuability for checking ?
In advance thanks for your time.
You can make use of interceptors by matching the request path.
<mvc:interceptors path-matcher="/someRequest/*">
<bean class="className" autowire="constructor"/>
</mvc:interceptors>
In path-mathcer you can specify one type of url,so that it will execute whatever you require.
Controller:
#Controller
#RequestMapping(value = {"someRequest/user"})
public class UserController {
Interceptor: Before processing someRequest/user you can use a interceptor like below, in 3 ways you can use->afterCompletion ,preHandle,postHandle.
In you case code would be written in preHandle method
public class SomeRequestIntercept implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object,
Exception exception) throws Exception {
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3)
throws Exception {
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
System.out.println("before processing someRequest/**");
return true;
}
}

advise controller method *before* #Valid annotation is handled

I am adding rate-limiting to a restful webservice using Spring MVC 4.1.
I created a #RateLimited annotation that I can apply to controller methods. A Spring AOP aspect intercepts calls to these methods and throws an exception if there have been too many requests:
#Aspect
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class RateLimitingAspect {
#Autowired
private RateLimitService rateLimitService;
#Before("execution(* com.example..*.*(.., javax.servlet.ServletRequest+, ..)) " +
"&& #annotation(com.example.RateLimited)")
public void wait(JoinPoint jp) throws Throwable {
ServletRequest request =
Arrays
.stream(jp.getArgs())
.filter(Objects::nonNull)
.filter(arg -> ServletRequest.class.isAssignableFrom(arg.getClass()))
.map(ServletRequest.class::cast)
.findFirst()
.get();
String ip = request.getRemoteAddr();
int secondsToWait = rateLimitService.secondsUntilNextAllowedAttempt(ip);
if (secondsToWait > 0) {
throw new TooManyRequestsException(secondsToWait);
}
}
This all works perfectly, except when the #RateLimited controller method has parameters marked as #Valid, e.g.:
#RateLimited
#RequestMapping(method = RequestMethod.POST)
public HttpEntity<?> createAccount(
HttpServletRequest request,
#Valid #RequestBody CreateAccountRequestDto dto) {
...
}
The problem: if validation fails, the validator throws MethodArgumentNotValidException, which is handled by an #ExceptionHandler, which returns an error response to the client, never triggering my #Before and therefore bypassing the rate-limiting.
How can I intercept a web request like this in a way that takes precedence over parameter validation?
I've thought of using Spring Interceptors or plain servlet Filters, but they are mapped by simple url-patterns and I need to differentiate by GET/POST/PUT/etc.
I eventually gave up on trying to find an AOP solution and created a Spring Interceptor instead. The interceptor preHandles all requests and watches for requests whose handler is #RateLimited.
#Component
public class RateLimitingInterceptor extends HandlerInterceptorAdapter {
#Autowired
private final RateLimitService rateLimitService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (HandlerMethod.class.isAssignableFrom(handler.getClass())) {
rateLimit(request, (HandlerMethod)handler);
}
return super.preHandle(request, response, handler);
}
private void rateLimit(HttpServletRequest request, HandlerMethod handlerMethod) throws TooManyRequestsException {
if (handlerMethod.getMethodAnnotation(RateLimited.class) != null) {
String ip = request.getRemoteAddr();
int secondsToWait = rateLimitService.secondsUntilNextAllowedInvocation(ip);
if (secondsToWait > 0) {
throw new TooManyRequestsException(secondsToWait);
} else {
rateLimitService.recordInvocation(ip);
}
}
}
}
Add the following controller advice in your application.
#ControllerAdvice
public class ApplicationControllerAdvice {
#InitBinder
#RateLimited
protected void activateBeanPropertyAccess(DataBinder dataBinder) {
dataBinder.initBeanPropertyAccess();
}
}
The #RateLimited should call the class RateLimitingAspect. So, after this all the constraints validator will be called.
See if it's feasible for you to implement similar logic for ##AfterThrowing advice as well which will have similar pointcut.

HTTP Status 405 - HTTP method is not supported by this URL

I have the following servlet:
public class MyServlet extends HttpServlet {
private static final long serialVersionUID = 16252534;
private static int ping = 3000;
private Thread t;
private static boolean shouldStop = false;
#Override
public void init() throws ServletException {
super.init();
t = new Thread(new Runnable() {
#Override
public void run() {
while(!shouldStop) {
System.out.println("Now:" + System.currentTimeMillis());
try {
Thread.sleep(ping);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
super.doGet(req, resp);
System.out.println("doGet");
PrintWriter out = resp.getWriter();
out.println("<html><h1>It works!!</h1></html>");
}
#Override
public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
super.service(req, resp);
System.out.println("service");
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
System.out.println("doPost");
}
#Override
public void destroy() {
super.destroy();
System.out.println("Destroy servlet");
shouldStop = true;
}
}
Which is mapped as follows in my web.xml:
<display-name>MyServer</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.myserver.MyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/MyServlet</url-pattern>
</servlet-mapping>
When I open my browser (Chrome) on http://localhost:8080/MyServer/MyServlet, then I see "service" from doService() being logged on console and my thread works correctly, however I don't see "It Works" from doGet() being logged and I get the following error in the browser:
HTTP method GET is not supported by this URL
How is this caused and how can I solve it?
This is the default response of the default implementation of HttpServlet#doXxx() method (doGet(), doPost(), doHead(), doPut(), etc). This means that when the doXxx() method is not properly being #Overriden in your servlet class, or when it is explicitly being called via super, then you will face a HTTP 405 "Method not allowed" error.
So, you need to make sure that you have the doXxx() method properly declared conform the API, including the #Override annotation just to ensure that you didn't make any typos. E.g.
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ...
}
And you also need to make sure that you don't ever call super.doXxx() in your servlet method:
super.doGet(request, response);
Your servlet has this. Just get rid of this line and your problem shall disappear.
The HttpServlet basically follows the template method pattern where all non-overridden HTTP methods returns this HTTP 405 error "Method not supported". When you override such a method, you should not call super method, because you would otherwise still get the HTTP 405 error. The same story goes on for your doPost() method.
This also applies on service() by the way, but that does technically not harm in this construct since you need it to let the default implementation execute the proper methods. Actually, the whole service() method is unnecessary for you, you can just remove the entire method from your servlet.
The super.init(); is also unnecessary. It's is only necessary when you override the init(ServletConfig), because otherwise the ServletConfig wouldn't be set. This is also explicitly mentioned in the javadoc. It's the only method which requires a super call.
Unrelated to the concrete problem, spawning a thread in a servlet like that is a bad idea. For the correct approach, head to How to run a background task in a servlet based web application?
you have overridden the service method which is responsible to delegate the call to doGet or doPost. see this for more details
Also get rid of super.doxxx(..) calls from each method.
Don't override the service method and you should see, "It Works" from doGet.

In spring mvc 3, how to write a cookie while returning a ModelAndView?

My controller method is returning a ModelAndView, but there is also a requirement to write a cookie back to client. Is it possible to do it in Spring? Thanks.
If you add the response as parameter to your handler method (see flexible signatures of #RequestMapping annotated methods – same section for 3.2.x, 4.0.x, 4.1.x, 4.3.x, 5.x.x), you may add the cookie to the response directly:
Kotlin
#RequestMapping(["/example"])
fun exampleHandler(response: HttpServletResponse): ModelAndView {
response.addCookie(Cookie("COOKIENAME", "The cookie's value"))
return ModelAndView("viewname")
}
Java
#RequestMapping("/example")
private ModelAndView exampleHandler(HttpServletResponse response) {
response.addCookie(new Cookie("COOKIENAME", "The cookie's value"));
return new ModelAndView("viewname");
}
Not as part of the ModelAndView, no, but you can add the cookie directly to the HttpServletResponse object that's passed in to your controller method.
You can write a HandlerInterceptor that will take all Cookie instances from your model and generate the appropriate cookie headers. This way you can keep your controllers clean and free from HttpServletResponse.
#Component
public class ModelCookieInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView modelAndView) throws Exception {
if (modelAndView != null) {
for (Object value : modelAndView.getModel().values()) {
if (value instanceof Cookie)
res.addCookie((Cookie) value);
}
}
}
}
NB . Don't forget to register the interceptor either with <mvc:interceptors> (XML config) or WebMvcConfigurer.addInterceptors() (Java config).
RustyX's solution in Java 8:
#Component
public class ModelCookieInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView modelAndView) throws Exception{
if (modelAndView != null) {
modelAndView.getModel().values().stream()
.filter(c -> c instanceof Cookie)
.map(c -> (Cookie) c)
.forEach(res::addCookie);
}
}
}

Resources