I'm using an event-scope component as a view controller. I need to have an "init" method on that controller, where I can do authorization checks, load some entities from JPA, etc.
Problem is, if I choose to do that in a #Create method, parameters defined in page.xml are not yet set (via GET). #RequestParameter fields get set, but they are less flexible - can't use them in inner classes of the component, or just somewhere deeper down in a bean path. For example, I can direct name=abc to #{controller.user.name}, but can only use a component-level setter with #RequestParameter.
Is there another way to do an "init" method, where all request parameters are set, then?
Couldn't find a solution, after all. Moving project to Spring MVC.
maybe u can try this in u page.xml
<param name="xxx" value="#{component.xxx}"/>
<action execute="#{component.init}"/>
Related
I want to retrieve the http method of the spring's <form:form> tag from inside a RequestDataValueProcessor in order to generate different hidden fields depending on it. I have access to the form action, but it seems there is no way to retrieve the http method of the form.
I am using Spring 3.2.
Edit: I have added an example.
Imagine the form below being retrieved with GET. What I want is to read the method parameter of the <form:form> tag in order to add a hidden field depending on the method. Obviously, I can't use request.getMethod() because this would return the method of the original request, not the method of the form being processed by RequestDataValueProcessor.
<form:form action="/foo/bar" method="post" modelAttribute="${modelAttribute}" >
<form:input path="myField" />
<input id="proceed" type="submit" value="Save" />
</form:form>
It seems that there is a pending issue to allow access to the form method in RequestDataValueProcessor.
This is the issue: https://jira.springsource.org/browse/SPR-10041
Definitely an open issue on Spring, but doesn't look like its going to be addressed soon.
Here is what you can do.. its not straight forward though, so weigh the cost vs benefit before you start :)
RequestDataValueProcessor will not work for you, you already know that. The way the FormTag is programmed, it calls specific methods (hooks) from the value processor at specific times, during the execution of the form tag.
You can create your own custom form tag, which will do everything Spring's form tag does, either by composition or inheritence. But in addition, you can call your own Value Processor esque class when the form method is detected by the tag. This class can then decide what additional steps you want to take and you can then use the regular ValueProcessor to add hidden fields as you see fit.
These questions talk about how to extend spring's tags, by creating your own: SpringMVC Custom Form Tags and Create a custom tag library which extends the Spring tag library.
Also look at the source code of the FormTag https://github.com/SpringSource/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java. See how the protected processAction method calls the value processor hook, you'll have to do something similar, but in the getMethod method.
Hope this answer makes sense, let me know if you want me to re-phrase or elaborate any point.
Happy programming!
A little more information would be helpful such as what version of Spring you are using.
From the Spring 3.2 API Documentation for RequestDataValueProcessor, I see that there are four (4) methods that you could possibly be working within. In each of those, you have access to the HttpServletRequest.
Accordingly, you have access to whatever HTTP Method the inbound request was made with by calling (link):
request.getMethod()
This should give you exactly what you're looking for.
I have a quick question on scope of ModelAttributes.
Dev. Env: Spring MVC 3.1/Java 6/JSP w/JSTL for Views
In my controller, I add an attribute to the model via
model.addAttribute(“appForResubmission”, appForResubmission);
In the JSP(served out in response to a GET request) I read it’s contents as:
${appForResubmission.appId}
— works fine and the data is shown on JSP as expected.
Upon submission of the JSP, in the same controller in a different method(in response to a PUT request), I try to read the attributes from the Model for any changes and I am doing this as
#ModelAttribute(“appForResubmission”) Application app
in the method signature.
However, all I get is a new Application object when I try to interrogate the object for data. Spring’s documentation says this kind of instantiation of a new object happens when the requested attribute does not exist in the Model.
What would cause the attribute to be lost? Any ideas? I am suspecting it is a scope issue someplace but I am not sure where the problem could be.
Any pointers you could provide is greatly appreciated?
Thank you,
M. Reddy
The scope of a modelattribute is the request, internally it is just equivalent to HttpSerletRequest.setAttribute("model", model).
If you want the model to be available in a different controller you probably have two options, one is to reconstruct it, based on what you submit to the controller or using your persistent source. The second option is for specific model attributes to be added to the session using #SessionAttribute({'modelname'}), but just be careful that you have to call SessionStatus.complete to remove the model added to the session later.
I have been developing with Spring MVC in a Servlet environment for quite some time, mostly on Spring 3.1.x and 3.2.x, but recently moved jobs to a company that uses a mixture of Portlets and Servlets using Spring 3.0.6.
Fist, there seems to be two versions of DefaultAnnotationHandlerMapping, one for Portlets and one for Servlets. My limited knowledge of Portlets makes me believe they are just an extention of Servlets, so I can vaguely understand the need to have two separate HandlerMappings, one to handle generic Servlets, the other to handle specifics to Portlest. What is throwing me is how they each seems to behave differently with regaurds to how they handle the #RequestMapping annotations, and how my new employer has chosen to set things up.
In my past, I have had one DispatchHandler for the entire application. I know you can have multiple DispatchHandlers, but I never saw the benefit past segregation of sub-context XMLs. Anyhow, in the past I would have a DispatchHandler mapped to "/", and then each Controller set a mapping on top using #RequestMapping("/someUrl") and then another #RequestMapping on each handler method further extending the URL mapping. So, from a browser I would get something like:
http://somehost.com/myWar/myController/myMethod
where the first "/myWar" is mapped to the WAR deployment, the next "/" is mapped to the DispatchServlet, "/myController" is the Controller's mapping, and anything after is matched to some method using a combination of URL signature, request method, parameters, etc.
Now lets evaluate what I am seeing at my new firm. First, each Controller gets its own DispatchServlet, so there is like 20-30 DispatchServlets defined, all with different base URIs mapped, some with the same base Application Context XML, some with different ones, all using the same Application Context from the ContextLoaderListener. Each of the subsequent DispatchServlet's path is the same as the #RequestMapping on their "mapped" Controller, so in the above example DispatchServlet would have the path "/myController" and the Controller would have #RequestMapping("/myController"). On top of that, at the Controller's method level, the value specified on #RequestMapping seems to totally ignore any additional pathing applied to it.
So, for instance, if I have a Controller with #RequestMapping("/myController"), followed by a method with #RequestMapping("/edit"), hitting the URL http://somehost.com/myWar/myContoller/edit gives me a 404. However, if I change the method-level #RequestMapping to #RequestMapping(parameter="actionMethod=edit"), and the URL to http://somehost.com/myWar/myController?actionMethod=edit, it maps just fine and I get my page. Further, if I change my method-level #RequestMapping to #RequestMapping(value="/edit",params="actionMethod=view") neither http://somehost.com/myWar/myController?actionMethod=edit nor http://somehost.com/myWar/myController/edit?actionMethod=edit work at all. The problem is that now to get to anything besides the default mapping at the method level requires me to take on "?actionMethod=edit", instead of simply extending the URI with the added paths. Also, it prevents me from using a RESTful approach, since now I can't define something like #RequestMapping("/somePath/{id}").
I have so much confussion in my brain, like why is the DispatchServlet mapping in the web.xml overlapping the Controllers #RequestMapping, and why is the #RequestMapping at the Controller's method level not working when I set the value property to a valid URI mapping, but works fine when I set some parameter mapping?
Oh, and sorry for the long posting. I just want to get a better understanding of what is happening here so I can either deal with it with an intellegent approach, or go back to my new group and say "this should be changed, and this is why". I can extend this lengthy question with some simplified code examples if that helps.
I am creating a custom route by subclassing RouteBase. I have a dependency in there that I'd like to wire up with IoC. The method GetRouteData just takes HttpContext, but I want to add in my unit of work as well....somehow.
I am using StructureMap, but info on how you would do this with any IoC framework would be helpful.
Well, here is our solution. Many little details may be omitted but overall idea is here. This answer may be a kind of offtop to original question but it describes the general solution to the problem.
I'll try to explain the part that is responsible for plain custom HTML-pages that are created by users at runtime and therefore can't have their own Controller/Action. So the routes should be either somehow built at runtime or be "catch-all" with custom IRouteConstraint.
First of all, lets state some facts and requirements.
We have some data and some metadata about our pages stored in DB;
We don't want to generate a (hypothetically) whole million of routes for all of existing pages beforehand (i.e. on Application startup) because something can change during application and we don't want to tackle with pushing the changes to global RouteCollection;
So we do it this way:
1. PageController
Yes, special controller that is responsible for all our content pages. And there is the only action that is Display(int id) (actually we have a special ViewModel as param but I used an int id for simplicity.
The page with all its data is resolved by ID inside that Display() method. The method itself returns either ViewResult (strongly typed after PageViewModel) or NotFoundResult in case when page is not found.
2. Custom IRouteConstraint
We have to somewhere define if the URL user actually requested refers to one of our custom pages. For this we have a special IsPageConstraint that implements IRouteConstraint interface. In the Match() method of our constraint we just call our PageRepository to check whether there is a page that match our requested URL. We have our PageRepository injected by StructureMap. If we find the page then we add that "id" parameter (with the value) to the RouteData dictionary and it is automatically bound to PageController.Display(int id) by DefaultModelBinder.
But we need a RouteData parameter to check. Where we get that? Here comes...
3. Route mapping with "catch-all" parameter
Important note: this route is defined in the very end of route mappings list because it is very general, not specific. We check all our explicitly defined routes first and then check for a Page (that is easily changeable if needed).
We simply map our route like this:
routes.MapRoute("ContentPages",
"{*pagePath}",
new { controller = "Page", action = "Display" }
new { pagePath = new DependencyRouteConstraint<IsPageConstraint>() });
Stop! What is that DependencyRouteConstraint thing appeared in mapping? Well, thats what does the trick.
4. DependencyRouteConstraint<TConstraint> class
This is just another generic implementation of IRouteConstraint which takes the "real" IRouteConstraint (IsPageConstraint) and resolves it (the given TConstraint) only when Match() method called. It uses dependency injection so our IsPageConstraint instance has all actual dependencies injected!
Our DependencyRouteConstraint then just calls the dependentConstraint.Match() providing all the parameters thus just delegating actual "matching" to the "real" IRouteConstraint.
Note: this class actually has the dependency on ServiceLocator.
Summary
That way we have:
Our Route clear and clean;
The only class that has a dependency on Service Locator is DependencyRouteConstraint;
Any custom IRouteConstraint uses dependency injection whenever needed;
???
PROFIT!
Hope this helps.
So, the problem is:
Route must be defined beforehand, during Application startup
Route's responsibility is to map the incoming URL pattern to the right Controller/Action to perform some task on request. And visa versa - to generate links using that mapping data. Period. Everything else is "Single Responsibility Principle" violation which actually led to your problem.
But UoW dependencies (like NHibernate ISession, or EF ObjectContext) must be resolved at runtime.
And that is why I don't see the children of RouteBase class as a good place for some DB work dependency. It makes everything closely coupled and non-scalable. It is actually impossible to perform Dependency Injection.
From now (I guess there is some kind of already working system) you actually have just one more or less viable option that is:
To use Service Locator pattern: resolve your UoW instance right inside the GetRouteData method (use CommonServiceLocator backed by StructureMap IContainer). That is simple but not really nice thing because this way you get the dependency on static Service Locator itself in your Route.
With CSL you have to just call inside GetRouteData:
var uow = ServiceLocator.Current.GetService<IUnitOfWork>();
or with just StructureMap (without CSL facade):
var uow = ObjectFactory.GetInstance<IUnitOfWork>();
and you're done. Quick and dirty. And the keyword is "dirty" actually :)
Sure, there is much more flexible solution but it needs a few architectural changes. If you provide more details on exactly what data you get in your routes I can try to explain how we solved our Pages routing problem (using DI and custom IRouteConstraint).
I'm running Spring MVC 3.x and I have one Controller with RequestMapping annotations (DefaultAnnotationHandlerMapping) and I have one ServletForwardingController with some additional mappings via SimpleUrlHandlerMapping. When I boot up my application, I see the following:
...
13:24:17,747 INFO [DefaultAnnotationHandlerMapping] Mapped URL path [/{query}] onto handler [com.foo.controllers.BarController#1b20a36]
13:24:17,997 INFO [SimpleUrlHandlerMapping] Root mapping to handler [org.springframework.web.servlet.mvc.ParameterizableViewController#598535]
13:24:18,044 INFO [SimpleUrlHandlerMapping] Mapped URL path [/spring*.ftl] onto handler [org.springframework.web.servlet.mvc.ServletForwardingController#56eb62]
13:24:18,044 INFO [SimpleUrlHandlerMapping] Mapped URL path [/shared-resources/**] onto handler [org.springframework.web.servlet.mvc.ServletForwardingController#56eb62]
...
My BarController is of course catching all requests (like /spring_en_US.ftl), but I want it to be tried last. In other words, I want the SimpleUrlHandlerMapping to take priority over the DefaultAnnotationHandlerMappings in my application.
The best solution I found was to replace mvc:annotation-config with explicit DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter bean configuration. I was then able to set the order property on both SimpleUrlHandlerMapping (order = 0) and DefaultAnnotationHandlerMapping (order = 1). This set the priority of the handlers as I needed.
I tried to reorder mvc:annotation-config below SimpleUrlHandlerMapping, but it didn't change the handler priority as needed.
DispatcherServlet should consult the various HandlerMapping beans in the order in which they're declared in the beans file. The first one that says "yes, I have a mapping for that" gets the prize.
If you're manually declaring a SimpleUrlHandlerMapping and DefaultAnnotationHandlerMapping, then make sure they're in the right order.
If you're not declaring them yourself, then Spring will use the defaults, which is a BeanNameUrlHandlerMapping followed by a DefaultAnnotationHandlerMapping.