I've got this issue, recently I read about the REST arquitecture and it makes a perfect sense, so I'd like to achieve a RESTful web application.
Now, I'm following the Front Controller pattern that means that all of the URL mappings go to the controller.java servlet, I map the by specific URLs, not by using the /* wildcard,
the controller implements the four HTTP methods POST,GET,PUT,DELETE, each method calls the controllers service method and there I determine based on the HttpServletRequest and pathInfo the action to execute.
Controller.java
#Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
IAction action;
View view;
try {
action = ActionFactory.produceAction(req);
view = action.execute(req, resp);
switch (view.getDispatchMethod()) {
case REDIRECT:
resp.sendRedirect(resp.encodeURL(view.getResource()));
break;
case FORWARD:
req.getRequestDispatcher(view.getResource()).forward(req, resp);
break;
case INCLUDE:
req.getRequestDispatcher(view.getResource()).include(req,resp);
break;
default:
}
} catch (ActionFailedException uae) {
req.setAttribute("ActionName", "Action");
req.setAttribute("FailCause", uae.getMessage());
req.getRequestDispatcher(VIEW_FAIL.getResource()).forward(req, resp);
}
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.service(req, resp);
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.service(req, resp);
}
#Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.service(req, resp);
}
#Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.service(req, resp);
}
I've run into a particular issue when loading a specific order by the URI /orders/*, it is mapped to the controller servlet, the the action is executed and I load the appropriate order the action returns a View.java class
//ommited accessors and mutators for brevety.
public class View {
public enum DispatchMethod {
INCLUDE, FORWARD, REDIRECT
}
private DispatchMethod dispatchMethod;
private String resource;
public View(DispatchMethod dispatchMethod, String resource) {
this.dispatchMethod = dispatchMethod;
this.resource = resource;
}
}
Then the request is dispatched according to the getDispatchMethod() of the returned view.
Now, here is where the loop gets triggered, I use the following URL, myapp/orders/78965 /orders/* gets mapped to controller.java the appropriate action is executed and the correct order is found by the pathInfo() the returned view is new View(View.DispatchMethod.FORWARD,"order_details.jsp") the problem is that with the three available dispatch methods REDIRECT,FORWARD and INCLUDE a request is re-triggered on the URL and so on and on and on I never reach the order_details.jsp that renders the data.
So, how would you avoid the looping, as I'd like to preserve the URI displaying the order number I use the forward method, also, I'd like to do it using servlets, I've heard of the UrlRewriteFilter maybe in the future, but right now, how would it be done using "Plain Vanilla" since I'm using the Front Controller pattern, will it be necessary to add an additional servlet in the /orders/ URI ?
Any help or insights is truly appreciated.
EDIT 1:
Pasted the source code of the controller, a very basic one, I have my suspicions that the way the service method calls all of the overriden do[Method] of the servlet is triggering the loop and that it may be solved by splittig them.
Implementing a RESTful HTTP interface in Java is a lot easier using a JAX-RS implementation like RESTEasy or Jersey.
Using a Front Controller to dispatch requests to the right resource is a good approach, it's exactly the approach taken by these JAX-RS frameworks. I fear you may be re-inventing the wheel here by writing a bespoke URL parsing and dispatching mechanism when this can be taken off-the-shelf.
JAX-RS is a lightweight way to expose resources. By using a couple of simple annotations you can expose a REST interface without any plumbing required. For example:
public class Order {
#GET
#Path("/orders/{orderId}")
#Produces("text/html")
public void getOrder(#Context HttpServletResponse response,
#Context HttpServletRequest request,
#PathParam("orderId") String orderId) throws ServletException, IOException {
// ... create view and add to request here
request.getRequestDispatcher("orders.jsp").forward(request, response);
}
}
You can see how simple it is to attach this class to a URL path (using the #Path annotation), and how easily you can parse values from the URL using #PathParam. Since you get all the plumbing/dispatching/parsing off-the-shelf, you can concentrate on the bits of your app that are specific to your domain (such as what an order contains).
Related
I'm developing a web application with Java and Spring Boot. What I would like to do is to add an object into the Model every time a request is received. Let me explain better what I'm doing and why I need it.
The application is an eCommerce and I need every time a page is loaded the number of items inside the cart and the number of notifications a user has got. These information are displayed inside the menu in all the pages of the web app. Right now I'm requesting these information to through an ajax call after the page has been loaded. I would like to automatically add these information inside the Model and render and return all the pages with them already present without making any further request.
I googled it and I found out that a way to solve this problem is to use an Interceptor.
I implemented it following this tutorial but the only problem is that preHandle, postHandle and afterCompletion get called not only with the page requests but also with other kind of content like images, videos etc.
#Component
public class ProductServiceInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Pre Handle method is Calling: " + request.getRequestURI());
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Post Handle method is Calling");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
System.out.println("Request and Response is completed");
}
}
When registering your interceptors in WebMvcConfigurerAdapter, you can also define a path pattern to include or exclude.
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new YourInterceptor()).excludePathPatterns("/path/to/your/static/resources/*");
}
Full example is available here : https://www.concretepage.com/spring/spring-mvc/spring-mvc-handlerinterceptor
In an old version of graphql-java-servlet I used to extend SimpleGraphQLServlet and then override GraphQLContext createContext( Optional request, Optional response ) to add a cookie to the response. I would also override GraphQLErrorHandler getGraphQLErrorHandler() to do some custom error handling.
I'm now trying to do a large jump in version to graphql-java-servlet 6.x.
As of graphql-java-servlet 6.x, SimpleGraphQLServlet is gone. There now is a SimpleGraphQLHttpServlet, which I can't use directly.
Unfortunately, though, the github documentation is way out of date and still suggests using SimpleGraphQLServlet even though it is long gone. There are some builders and I can find some very simplistic references outside of the github documentation, but none of them cover my use cases.
I don't want to do anything fancy, but I need to be able to add a cookie to the response and do some custom error handling.
How can I do this in graphql-java-servlet 6.x? I can't seem to find any clarity on this.
There is GraphQLServletListener. It is also described in project docs, although it is a bit wrong (no OperationCallback in code).
Anyway, here is a working piece of code (used com.graphql-java-kickstart:graphql-java-servlet:6.1.4):
GraphQLSchema schema = getSchema();
List<GraphQLServletListener> listeners = new ArrayList<GraphQLServletListener>();
GraphQLServletListener lstnr = new GraphQLServletListener(){
public RequestCallback onRequest(HttpServletRequest request, HttpServletResponse response) {
System.out.println("onRequest:" + request.getRequestURI());
//TODO cookies here
response.addCookie(new Cookie("sample","test"));
return new RequestCallback() {
#Override
public void onSuccess(HttpServletRequest request, HttpServletResponse response) {
System.out.println("onSuccess:" + request.getRequestURI());
}
#Override
public void onError(HttpServletRequest request, HttpServletResponse response, Throwable throwable) {
//TODO add some error handling here
System.out.println("onError:" + request.getRequestURI());
}
#Override
public void onFinally(HttpServletRequest request, HttpServletResponse response) {
System.out.println("onFinally:" + request.getRequestURI());
}
};
}
};
listeners.add(lstnr);
SimpleGraphQLHttpServlet servlet = SimpleGraphQLHttpServlet.newBuilder(schema)
.withListeners(listeners)
.build();
Is it possible to get HttpServletRequest from the ServletContext?
Is it possible to get HttpServletRequest from the ServletContext?
No.
The ServletContext represents the application. The application can cover many sessions and requests. But you can't get the "currently running" request or session via the ServletContext. Detail on how servlets and scopes work can be found in this related answer: How do servlets work? Instantiation, sessions, shared variables and multithreading.
You're unfortunately not clear on the concrete functional requirement where you need this solution. You apparently have a ServletContext at hands somehow in an instance of the class of interest, but not a HttpServletRequest. It's hard to propose an answer showing the right way how to grab the HttpServletRequest in an instance of such class anyway. Decent MVC frameworks like JSF and Spring MVC have ways to grab the HttpServletRequest associated with the current thread in any class you want.
In case you're not using a MVC framework and thus can't use its facilities, then you can achieve this manually by storing the request (and response) as a ThreadLocal<T> in the current thread via a servlet filter.
Here's a kickoff example how such a thread local context class can look like:
public final class YourContext implements AutoCloseable {
private static ThreadLocal<YourContext> instance = new ThreadLocal<>();
private HttpServletRequest request;
private HttpServletResponse response;
private YourContext(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
public static YourContext create(HttpServletRequest request, HttpServletResponse response) {
YourContext context = new YourContext(request, response);
instance.set(context);
return context;
}
public static YourContext getCurrentInstance() {
return instance.get();
}
#Override
public void close() {
instance.remove();
}
// ... (add methods here which return/delegate the request/response).
}
You can create (and close!!) it in a servlet filter as below.
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try (YourContext context = YourContext.create(request, response)) {
chain.doFilter(request, response);
}
}
Do note that closing is very important. Otherwise the thread will get polluted after it has done its job and will be recycled for a different request or even a completely different purpose. In case you aren't on Java 7 yet and thus can't use try-with-resources statement as above, then use a try-finally block.
Then, in any artifact which is invoked by the same thread/request (i.e. other filters, any servlets, any beans/classes (in)directly invoked by those artifacts, etc), you can obtain the HttpServletRequest associated with the current thread as below:
YourContext context = YourContext.getCurrentInstance();
HttpServletRequest request = context.getRequest();
// ...
Or, better create a delegate method, depending on whatever you'd like to do with the current request, such as obtaining the request locale:
YourContext context = YourContext.getCurrentInstance();
Locale requestLocale = context.getRequestLocale();
// ...
As a real world example, Java EE's MVC framework JSF offers exactly this possibility via FacesContext.
FacesContext context = FacesContext.getCurrentInstance();
Locale requestLocale = context.getExternalContext().getRequestLocale();
// ...
I'm currently writing a little dynamic web-application in Java.
The application is supposed to be an event-platform where you can create a user-account, log in, and then you can see all open events (in a later iteration, users can create/participate in those events).
Right now, the structure of the web-app could be (simplified) described like this:
Register-Servlet -> Register.jsp
|
V
Login-Servlet -> Login.jsp
|
V
Main-page-Servlet -> Main.jsp
So right now, a user could go to Login.jsp, his login-information would be sent to the Login-Servlet, which would validate it and then send it to the Main-Page-Servlet.
The Main-Page-Servlet then (after validating login again) gets all current events from a database, attaches it to the request, and forwards it to the Main.jsp, which displays it for the user to see.
Now, if a user wants to access the Main.jsp directly (without coming from the Main-Page-Servlet), it obviously can not display the available events. The workaround I'm using currently is doing a null-check to see if the events are there, and if not, redirect to the Main-Page-Servlet.
It bothers me to solve my problem like that, as I don't think that's the best practice and I think it will just create a lot of other problems the bigger my application gets.
My first thought about this was, that it might be useful if I could simply "hide" all .jsp's from the user, so the user would be landing on servlets only and could not access the .jsp's in a different way.
Is there a way to do that? Or, if not, what would be the best practice solution if I would be writing a professional enterprise-level application?
This can be handled in a Filter and there are great explanation and example in StackOverflow Servlet-Filter wiki.
Adapting the code there for your problem (note the addition and usage of the needsAuthentication method):
#WebFilter("/*")
public class LoginFilter implements Filter {
#Override
public void init(FilterConfig config)
throws ServletException {
// If you have any <init-param> in web.xml, then you could get them
// here by config.getInitParameter("name") and assign it as field.
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
String requestPath = httpServletRequest.getRequestURI();
if (needsAuthentication(requestPath) ||
session == null ||
session.getAttribute("user") == null) { // change "user" for the session attribute you have defined
response.sendRedirect(request.getContextPath() + "/login"); // No logged-in user found, so redirect to login page.
} else {
chain.doFilter(req, res); // Logged-in user found, so just continue request.
}
}
#Override
public void destroy() {
// If you have assigned any expensive resources as field of
// this Filter class, then you could clean/close them here.
}
//basic validation of pages that do not require authentication
private boolean needsAuthentication(String url) {
String[] validNonAuthenticationUrls =
{ "Login.jsp", "Register.jsp" };
for(String validUrl : validNonAuthenticationUrls) {
if (url.endsWith(validUrl)) {
return false;
}
}
return true;
}
}
I would recommend to move all the pages that require authentication inside a folder like app and then change the web filter to
#WebFilter("/app/*")
In this way, you can remove the needsAuthentication method from the filter.
There're several ways to do it such as servlet filter as above. I saw in some projects they use a simpler mechanism to do it by creating a common action (servlet). So instead of extends HttpServlet, all servlet will be extended the common action. And you can implement a lot of common stuffs such as authentication, validations, permissions...
Here's common action example:
public class CommonServlet extends HttpServlet {
................
................
protected boolean validate(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=UTF-8");
request.setCharacterEncoding("UTF-8");
String email = (String) request.getSession().getAttribute("email");
Object salaryGroup = request.getSession().getAttribute("SALARY_GROUP");
if (email == null || email.equals("")) {
request.setAttribute("err", "You have not logged in");
request.getRequestDispatcher("/login.jsp").forward(request, response);
return false;
}
................
................
}
public void setRoleAndValidate(HttpServletRequest request, HttpServletResponse response, String role)
throws ServletException, IOException {
if (!validate(request, response)) {
return;
}
setRoleCode(role);
}
................
................
}
Your action servlet will be as below:
#WebServlet("/employeeManager")
public class EmployeeManager extends CommonServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
request.setCharacterEncoding("UTF-8");
setRoleAndValidate(request, response, Permission.EMPLOYEE_LIST.toString());
String action = request.getParameter("action");
.....
Here's the simple implementation
I used to have an Open Session In Conversation Filter based on cookies for a JSF 2 app. Now I want to build the same mechanism but technology-agnostic. Reusing some code, I have written this in a class that extends OncePerRequestFilter:
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
UUID conversationId = lookupConversationOrCreateIfNecessary(request,
response);
log.debug("Binding conversation '{}' to request '{}'", conversationId,
request);
bindConversation(conversationId, request);
try {
filterChain.doFilter(request, response);
} finally {
log.debug("Unbinding conversation '{}' from request '{}'",
conversationId, request);
unbindConversation(conversationId, request);
}
}
Now, when I reach bindConversation(conversationId, request) I just add a request attribute which points to the conversationId which is mapped to a Hibernate Session.
Anyways, in JSF I can access the current request by using FacesContext.getCurrentInstance().getExternalContext().getRequest() and implemented a CurrentSessionContext using this. But in plain servlets how can I access the current request programmatically?
Note: I have been reading the OncePerRequestFilter javadocs and I found this:
As of Servlet 3.0, a filter may be invoked as part of a REQUEST or
ASYNC dispatches that occur in separate threads. A filter can be
configured in web.xml whether it should be involved in async
dispatches. However, in some cases servlet containers assume different
default configuration. Therefore sub-classes can override the method
shouldNotFilterAsyncDispatch() to declare statically if they [sic] shouuld
indeed be invoked, once, during both types of dispatches in order to
provide thread initialization, logging, security, and so on. This
mechanism complements and does not replace the need to configure a
filter in web.xml with dispatcher types.
So, would it be dangerous to use a ThreadLocal to achieve what I want?
As you mention in your question: using a ThreadLocal seems a good option. I don't see why it would be unsafe as soon as you use your filter for both REQUEST and ASYNC.
EDIT
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
UUID conversationId = lookupConversationOrCreateIfNecessary(request,
response);
log.debug("Binding conversation '{}' to request '{}'", conversationId,
request);
ConversationHolder.setId(conversationId);
bindConversation(conversationId, request);
try {
filterChain.doFilter(request, response);
} finally {
log.debug("Unbinding conversation '{}' from request '{}'",
conversationId, request);
ConversationHolder.clear();
unbindConversation(conversationId, request);
}
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return false; //to be sure both REQUEST and ASYNC are filtered
}
And the ConversationHolder
public class ConversationHolder extends ThreadLocal<UUID>{
private static ConversationHolder INSTANCE = new ConversationHolder();
public static void setId(UUID conversationId){
INSTANCE.set(conversationId);
}
public static UUID getId(){
return INSTANCE.get();
}
public static void clear(){
INSTANCE.remove();
}
}
Since conversationId is a local variable it won't be shared between request.
Since ConversationHolder is a ThreadLocal, the value you get from it during doFilter(...) will be correct. (except if you create new Thread by hand during your request processing, but it is not a recommended design)