I'm just new into Spring Framework tech and I'm making a website with few WebServices(Webstore, social media site etc.) included. What I want for it, is that every webservice would have it owns DispatcherServlet with own ViewResolver. So the structure of my projects looks like:
The Main site - DispatcherServlet #1 mapping only to http://example.com (yeah, i know i could just use static html site but it's for learning purposes.
Webservice no.1 e.g Webstore - DispatcherServlet #2 mapping only to
/webstore/*
Webservice no.2 e.g Social media site - DispatcherServlet #3 mapping only to /social/*
In configuration I'm using Java Classes, so what I did was:
Firstly I've Created AppConfig class that extends WebMvcConfigureAdapter and
implements ViewResolver for main site views. Next I've created An AppInitializer class which extends AbstractAnnotationConfigDispatcherServletInitializer class and inside of it I've configured RootConfigClass and servlet mapping
Secondly I've had problem - namely I couldn't create another class for Dispatcher Servlet #2 (couldn't have two classes which extended AbstractAnnotationConfigDispatcherServletInitializer ), what I've found was creating class which would implement an interface WebApplicationInitializer. Which I did and finally had working Second DispatcherServlet
Thirdly I've reapeted steps from second point and had Three working Dispatcher Servlets(in theory...).
I could access my main site via DispatcherServlet #1
(http://example.com)
I could access my webstore main site via DispatcherServlet #2 (http://example.com/webstore)
and I could access my webservice via DispatcherServlet #3(http://example.com/social)
but after all, when I've created another view pointing e.g. to login section (http://example.com/webstore/login) It didn't work at all. The errors said that Dispatcher Servlet #1 couldn't handle mapping request, where the address should be tied with Dispatcher Servlet #2. What I did to resolve this problem was changing The Dispatcher Servlet #2 configuration class part of servlet mapping from servlet.addMapping("/webstore/"); to servlet.addMapping("/webstore/*"); which I've read is not the best idea but It worked.
The problem appears when I would type address that Controller doesn't handle(doesn't exists) like (http://example.com/webstore/thisaddressdoesntexists), the view controller gives me back main default view from http://example.com/webstore, instead of 404 not found page. This is not the behavior I've intented to implement. Besides that little 'feature', everything works fine. All specyfic request are served with their specyfic DispatcherServlet. Finnal questions are:
How to prevent from loading default view, in this case scenario, when I'm pointing to non-existing url and load error code sites?
Is my concept even good at all? I mean using classes which implements WebApplicationInitializer for using more DispatcherServlets or there is another way? I'm only talking about Java-Based Config Classes, no xmls.
Is my concept even good at all? I mean using classes which implements
WebApplicationInitializer for using more DispatcherServlets or there
is another way? I'm only talking about Java-Based Config Classes, no
xmls.
For multiple servlets you need to have a single class which implements WebApplicationInitializer and there provide contextLoader class with all beans for the root context and define a separate class with all the beans for each servlet (usualliy the class which extends WebAppConfigurer). You also need to provide a mapping for each individual servlet. Have a look here how it's done as an example (the last 2 responses)
As regards to
Is my concept even good at all?
Why do you want to have three separate servlets? Are these three different apps bundled together? If that's the case I wouldn't put them in the same package but make three 3 different apps. You can also use a single Dispatchetservlet which maps to every url and have separate controllers for each component. That will reduce the complexity of the whole project. So it could be either way. Without knowing more about what you are trying to do no one can say it's right or wrong.
How to prevent from loading default view, in this case scenario, when
I'm pointing to non-existing url and load error code sites?
For error handling page have a look here. You would normally use #ResponseStatus for a specific HTTP error code or #ExceptionHandler for a general purpose error. In both cases you would need to redirect to a default error page.
Related
I am running a Jetty web server and I have a generic servlet filter to handle exceptions. As part of this, I want to log aggregated metrics about which servlets are causing the exceptions. Is there a way I can figure out which servlets defined in the web.xml match the servlet path of the current request?
I have a generic servlet filter to handle exceptions
I take you to mean that your filter extends javax.servlet.GenericFilter directly.
Is there a way I can figure out which servlets defined in the web.xml match the servlet path of the current request?
In any given servlet context there should not be more than one servlet mapped to a given servlet path, so in any given case you're looking for just one servlet, not multiple.
As far as I know or can tell, GenericFilter and the generic ServletRequests and ServletResponses with which it works do not provide a good way to access the information you are after. If you cast the request to HttpServletRequest, however, then you can obtain its HttpServletMapping (since Servlet 4.0 / Java EE 8)
, which contains the servlet name as declared in the webapp's deployment descriptor, among other information. Or the HttpServletRequest can directly provide the servlet path, too.
But if you are contemplating casting the requests, then you would probably be better off having your filter extend javax.servlet.http.HttpFilter, which would give you type-safe access. This is itself a subclass of GenericFilter, so it probably wouldn't be difficult to perform such a refactoring.
It seems like I can do everything that the Controller class do inside Spring Web-Flow, for example decision making and switching from page to page. To my understanding, its the C inside the MVC model. Am I correct about this?
So my question is, is there any advantage to still include a Controller class when using Spring Web-Flow?
If you need access to the request and response, an appropriate design might still include a controller while also having a flow.xml. For example, if you had this code:
HttpServletRequest request = (HttpServletRequest)context.getExternalContext().getNativeRequest();
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getNativeResponse();
It's more intelligible to put that in a controller rather than a service.
Also, if you want to register a custom editor, it might make sense to have the controller have this logic in the initBinder() method.
Spring Web Flow uses the Spring MVC framework. The DispatcherServlet handles the request. A FlowHandlerMapping is used to map the request to a particular Web Flow.
Web Flow is to solve the problem involved with controller logic that spans multiple-page navigation (a pageflow, or wizard).
Web Flow can eliminate the need for specialized controller classes to accomplish following a path of page transitions/form updates along a predefined workflow. If you don't need to do this, you can save yourself a lot of configuration/complexity just by using MVC.
I am trying to add some metric gathering to a Spring MVC app. Lets say I have a controller whose mapping is:
/User/{username}/Foobar
I want to gather metrics on all controller mapping invocations with the path. Right now I can create a handler/interceptor and look at the requests but that will give me:
/User/Charlie/Foobar
Which is not what I want. I want the controller mapping itself to log. and I don't want to have to add something to every controller. I'd also rather not use AOP if I can help it.
It turns out that Spring hangs the best matching controller pattern on the request itself. You can get this from within a handlerinterceptor like this:
(String)request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)
I can think of two choices:
It seems to me the results of the matching are obtained in the class org.springframework.web.servlet.handler.AbstractUrlHandlerMapping, which logs the patterns obtained (see line 266). I'd try enabling logging for that class and see if the output is helpful for your purposes.
(Complicated)
Extending org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping to override the lookupHandler method inherited from AbstractUrlHandlerMapping and logging/registering what you need. Accoding to this class documentation, you can register a different one so that the DispatcherServlet uses your version.
In Spring 3.2.x DefaultAnnotationHandlerMapping is deprecated so, a different class would have to be used.
I have a servlet which handles http get requests that I'd like to be able to share an object which is also used by a webservice im developing. They are both on the same tomcat server within the same web-app container. Im not sure best how to do it any ideas ?
You can share things across the webapp by storing them as attributes in the ServletContext (using setAttribute / getAttribute). You could create the object in an impelementation of ServletContextListener, store it in the ServletContext, and then retrieve it and use it from your web service and servlet.
I will expand on my comment here.
In the simplest case ServletContext.setAttribute/getAttribute would be fine. But some people rightly raised the questions about thread safety.
For this a better approach would be to store a shared POJO in a ServletContext during webapp initialization and get it wherever you need with ServletContext.getAttribute.
For this you need to implement ServletContextListener interface and declare it in your web.xml file.
<listener>
<listener-class>your.package.ServletContextListenerImpl</listener-class>
</listener>
This listener is called once when your webapp is loaded by the servlet container, and when it is about to be unloaded by the servlet container. In both cases it passes ServletContextEvent that has a handle to ServletContext. It is at that point that you want to set/removeAttribute that points to your shared object.
Because of this you may be certain that ServletContext.getAttribute will return a valid object when called from the Servlet.service or one of the do... methods.
As for attribute name, I would go with your Shared class classname. I would also add a static access method to your shared class to get it from the ServletContext, like this:
public class Shared
{
...
public static Shared fromServletContext ( final ServletContext context )
{
return (Shared) context.getAttribute( Shared.class.getName( ) );
}
}
The simplest option is create a Singleton - a class which allows only one instance in memory. Since you get it by calling a static method on the class itself it should be available to both the servlet and the WS
If my alternative understanding of the question is correct, the data which comes from request should be stored, and then retreived by the web-service. If this is supposed to run in a multi-user environment, you might consider using an in-memory database (HSQLDB) to temporarily store the data. Then you will be able to retrieve it with your web-service, based on some criteria I cannot foretell.
If this is application-specific data (accessible by all users (sessions)), then use ServletContext#set/getAttribute(). If this is user (session)-specific data, then use HttpSession#set/getAttribute().
Let the servlet class set the object in the desired scope (application or session) by a specific attribute key and let the webservice get the object from the desired scope by the same attribute key.
I think you rather need the HttpSession.
The singleton story makes no sense here.
Is there a way for me to instantiate the Spring MVC DispatcherServlet in code rather put it in the web.xml and have it be instantiated by the web server?
The reason for this is that I want to check a memCache to see if I have already recently rendered the page that is being requested and if so just return from the memCache, rather than going through Spring MVC and the controllers.
The ~2 second instantiation of the DispatcherServlet is significant because I am using Google App Engine and that might end up being an additional 2 seconds the user has to wait for their page.
I have tried
dispatcherServlet = new DispatcherServlet();
dispatcherServlet.init();
dispatcherServlet.service(request, response);
but I get this exception on the init call:
[java] java.lang.NullPointerException
[java] at org.springframework.web.servlet.HttpServletBean$ServletConfigPropertyValues.<init>(HttpServletBean.java:196)
[java] at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:114)
Basically, what I'm looking for is a way to instantiate a servlet in code without having to specify it in web.xml and not have to call
getServletConfig().getServletContext().getServlet("dispatcherServlet");
DispatcherServlet is a servlet, so you should call init(ServletConfig) instead of init() to initialize it.
Unless Google App Engine does something really weird, the DispatcherServlet is only instantiated once, on application startup.
If you want to cache page response as you mention, I would suggest implementing this as a HandlerInterceptor (which you can apply to any URL pattern you like), which gives you hooks to plug in logic in either pre- or post-invocation of your controller.