Package-based controller resolving in Spring MVC - java

I would like to know if it is possible to make Spring MVC work like Stripes when resolving controller classes.
Normally I can annotate a controller with a #RequestMapping in order to map it to its URL.
However my boss asked for a convention-over-configuration mechanism derived from Stripes, which we are progressively abandoning after years of employment.
You see, Stripes Actions are scanned in classpath under packages that contain stripes.action in their name. Each subpackage is a virtual directory and ultimately a class named SomethingAction will map to url Something.action.
Whan I need to do is as follows:
com.example.product.web.controllers.secure.admin.UserController mapping to /secure/admin/user
com.example.something.different.from.before.web.controllers.pub.WelcomeController mapping to /pub/welcome
Basically I'd like to make Spring MVC automagically map controllers according to their full class name instead of using #RequestMapping on every controller.
Important! I don't need exactly that naming convention (web.controllers) if Spring MVC has already a naming convention. I simply need one.
I have found no clue so far.
Thanks

ControllerClassNameHandlerMapping does it out-of-box what you are looking forward to.
Simply set the base package as com.example.product.web.controllers and the sub-packages would be mapped as path for you. To quote from API docs
Specify a base package like "com.mycompany.myapp" to include subpackages within that base package as path elements, e.g. generating the path "/mymodule/buyform" for the class name "com.mycompany.myapp.mymodule.BuyForm". Subpackage hierarchies are represented as individual path elements, e.g. "/mymodule/mysubmodule/buyform" for the class name "com.mycompany.myapp.mymodule.mysubmodule.BuyForm".
Do note that it is constrained to have only single base package. Incase there are different package hierarchies to scan, the behaviour of ControllerClassNameHandlerMapping has to be customized accordingly.

Related

Jersey 404 on two Resource Classes with similar #Path annotations

I have a RESTful Web service, using Drop Wizard 0.8.5 with Jersey 2.21. I have a resource class with is annotated with:
#Path("/mysite/somepath")
This class contains various methods, such as #GETs, #PUTS, etc., all working just fine.
Now, I have another resource class that is annotated with #Path("/mysite"). Within this resource class I have a need to add a few methods annotated with paths such as the following:
#Path("/somepath/dothis")
#Path("/somepath/dothat")
The resource classes all register just fine. However, when I make a call to the second class I get a 404, as it appears Jersey is looking for these methods in my first class. Is there a way to resolve this issue, other than changing my #Path annotations to avoid this naming conflict?
Jersey presumes each class has unique #Path expression associated with it. If you want to use the same #Path variable to two different resources, you either use two different path names, or you can combine the two classes into one class.

JAX-RS: Is it possible to have an externally configurable #PATH?

Is it possible to load the value for the #PATH annotation from configuration (web.xml, etc) for a given class?
#Path(<value loaded from config>)
public class myRestService {
...
Independent of JAX-RS: Annotations in Java are compile time constants so they can't be changed at runtime.
I don't know your use case but possible ways to change the values of the annotations are:
Replacing variables before compilation, e.g. through a maven plugin.
Adding the #Path annotations dynamically like described here.
Using one generic ResourceClass mapped to /* which decides which subresource should be returned.
No comment if one of these approaches makes sense as I don't know why you want to change them. As the URI names a resource I don't see any reason to change it. See also: Cool URIs don't change
Update: JAX_RS_SPEC-60 requests "A Dynamic way to register JAX-RS resources (not based on annotations)".
According to JAX-RS specification (here), there is no standard way to do this, I think.

struts2 conversation and validation

I am working on a struts2 project that has interdependent forms.
I found struts2-conversation, stepped through their simple-example
and understood the conversation mechanism this far (please correct me if I got something wrong):
The Controller is mapped in the struts.xml
It holds the serializable ConversationContext and the Storing-Service
The ConversationContext holds POJOs mapped on forms by naming convention
Now my question is where to put the validation?
In this structure the controller is only one extending ConversationSupport and thereby ActionSupport supplying the validate, prepare and addField- & ActionError methods.
But validating within the controller would mean to validate the whole context, which does not really serve the issue.
I tried validation through annotation within the POJOs, within the context as described above which gives me some NullPointerException as if the context wasn't flushed and I think the xml-validation approach of struts2 is just too stiff. (btw how to let the generated javascripts be minified before being served? And why is there so many options?)
Mark's conversation-interceptor approach had similar problems coming up which's workarounds I didn't really get. Maybe you can help me there.
If you would like to use annotations on your model classes, it works fine with the plugin (as do the other validation approaches).
To validate your model, add #VisitorFieldValidator to the getModel() method in your controller. In the example app, you would then also add #VisitorFieldValidator to the getContact() and getPreferences() methods. Then you can use the validation annotations on the fields you wish to validate.
The service in the example is just there as a simple example of using an injected service in a Struts2 controller and how that can integrate easily with the conversation framework, but it is not directly related or needed (and I would recommend using either Spring, Guice, or CDI for dependency injection in the real world).
The ConversationContext class is intended mostly for internal use by the framework. You should be able to avoid interacting with it by using the annotations and conventions. Unless you simply wish to be adventurous.
To use XML validation in the example app, you would have to change the package name to remove the "struts2" word in order for the Struts2 resource loading tool to load the XML.

How to map a Resource to an enum constant?

I'm currently refactoring a (former) monster of a method in a Spring-MVC controller. The method basically does the following things:
Digs up a few identifiers from the request
Gets an XML representation for a product identified by them from a cache
Uses an XSL stylesheet to produce a PDF (which it then stores to a cache and adds the key it can be found with to the Model).
I've been able to remove almost all duplicated logic by making a Product enum which contains all the other product specific things, but the locations of the XSL stylesheets are problematic. Previously they were configured as org.springframework.core.io.Resource-type properties of the controller bean, but now that the product specific things are in the enum, I would either need to map them somehow to the enum constants or find another solution for locating them.
I think it would be best to have the XSL as part of the enum since there's a 1-to-1 relationship between them, but there's no way to inject the resource there. Using the class loader to load the resource to the enum manually would work in tests, but would be problematic in production environment since the stylesheet files are not in the classpath there. Plus I wouldn't like to make the enum depend on any Spring stuff.
Any ideas on how to tackle this problem without making the enum and the controller too tightly coupled?
In the end I made the enum an inner class of the controller and added an abstract method called getXsl to it. The implementing enum constants now return the related Resource directly. I didn't have to change anything else since the Resource beans were already static.

Spring 3 Properties File Per Controller

I was wondering if Spring has a properties files mechanism similar to Struts2 where it looks for a properties file first in the same package as the controller and then moves up the package structure until it finds the properties file or property.
I want to define a property file per controller, but I rather not have to wire them up together, if possible. Is there some convention that can be followed that would associate the properties file with the controller? The properties file resolution should also work correctly when resolving locales.
For example, if I define a property called "title" in several prop files, I want the correct one to resolve in the JSP based on which controller handled the request.
ControllerA RETURNS ViewA USES PropA.title
ControllerB RETURNS ViewB USES PropB.title
I was successful in auto wiring a Property file to a controller's Model attribute and display values in JSP. I was also able to specify a ResourceBundleMessageSource in the configuration and then display values from it in JSP.
Out of the box, not that I know of.... but you can easily write one, if that's what you really want!
Spring has things like BeanFactoryPostProcessor which lets you do things like this with your BeanFactory/ApplicationContext. I'm thinking BeanFactoryPostProcessor would fit the bill here - you could 'post process' a bean by looking for a properties file on the classpath, grabbing the properties, and applying them to the bean.
I'll say this though - Spring is usually meant to be fairly non-invasive. If you want something like this to become part of your design, you might want to think of a way to do this in pure Java, rather than using Spring. For example, create your own Factory and implement and unit test it separately. Require your app to use this Factory to get your business objects.
In other words, you can probably do it in Spring - but it isn't always the best approach. It does sound like it could be the most convenient, in your case, but I haven't see the details of what you're setting and where. Just food for thought..

Categories

Resources