Spring - binding to an object rather than a String or primitive - spring

Let's say I have the following command object:
class BreakfastSelectCommand{
List<Breakfast> possibleBreakfasts;
Breakfast selectedBreakfast;
}
How can I have spring populate "selectedBreakfast" with a breakfast from the list?
I was figuring I'd do something like this in my jsp:
<form:radiobuttons items="${possibleBreakfasts}" path="selectedBreakfast" />
But this doesn't seem to work. Any ideas?
thanks,
-Morgan

The key to it all of this is the PropertyEditor.
You need to define a PropertyEditor for your Breakfast class and then configure the ServletDataBinder using registerCustomEditor in the initBinder method of your controller.
example:
public class BreakfastPropertyEditor extends PropertyEditorSupport{
public void setAsText(String incomming){
Breakfast b = yourDao.findById( Integer.parseInt(incomming));
setValue(b);
}
public String getAsText(){
return ((Breakfast)getValue()).getId();
}
}
note you'll be needing some null checking etc, but you get the idea. In your controller:
public BreakfastFooBarController extends SimpleFormController {
#Override
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
binder.registerCustomEditor(Breakfast.class, new BreakfastPropertyEditor(yourDao));
}
}
things to watch out for:
PropertyEditor's are not thread safe
if you need spring beans, either manually inject them or define them in spring as prototype scope and use method injection into your controller
throw IllegalArgumentException if the inbound parameter is not valid/not found, spring will convert this into a binding error correctly
hope this helps.
Edit (in response to the comment):
It looks a little strange in the given example because BreakfastSelectCommand doesn't look like an entity, I'm not sure what the actual scenario you have is. Say it is an entity, for example like Person with a breakfast property then the formBackingObject() method would load the Person object from the the PersonDao and return it as the command. The binding phase would then change the breakfast property depending on the selected value, such that the command that arrives in onSubmit has the breakfast property all set up.
Depending on the implementation of your DAO objects calling them twice or attempting to load the same entity twice doesn't actually mean that you will get two SQL statements being run. This applies particularly to Hibernate, where it guarantees that it will return the same object that is in it's session for a given identifier, thus running letting the binding attempt to load the Breakfast selection even through it hasn't changed shouldn't result in any undue overhead.

Related

Remove a Spring ConversionService across the entire application

I am running with Spring 3.2.18 (I know, it's old) with Spring MVC. My issue is that there is at least one default request parameter conversion (String -> List) that fails when there is actually only one item in the array, but it has commas. This is because the default conversion built into Spring will see it as a comma-separated list.
I am NOT using Spring Boot, so please avoid answers that specifically reference solutions using it.
I tried adding a #PostConstruct method to a #Confuguration class as follows:
#Configuration
public class MyConfig {
#Autowired
private ConfigurableEnvironment env;
#PostConstruct
public void removeConverters() {
ConfigurableConversionService conversionService = env.getConversionService();
conversionService.removeConvertible(String.class, Collection.class);
}
}
This runs on startup but the broken conversion still occurs. I put a breakpoint in this method, and it is called only once on startup. I verified that it removed a converter that matched the signature of String -> Collection.
The following works, but when I put a breakpoint in the #InitBinder method it acts like it gets called once for every request parameter on every request.
#ControllerAdvice
public class MyController {
#InitBinder
public void initBinder(WebDataBinder binder) {
GenericConversionService conversionService = (GenericConversionService) binder.getConversionService();
conversionService.removeConvertible(String.class, Collection.class);
}
}
As I said the second one works, but it makes no sense that the offending converter has to be removed for every request made to that method - let alone for every request parameter that method takes.
Can someone please tell me why this only works when the removal is incredibly redundant? Better yet, please tell me how I'm doing the one-time, application-scope removal incorrectly.

How to choose bean implementation at runtime for every http request

I am having two implementations of my component.
public interface MyComponent {
}
imple1
#Component("impCompf")
#Lazy
#RequestScope
public class ImpComp1 implements MyComponent {
}
imple2
#Component("impComps")
#Lazy
#RequestScope
public class ImpComp2 implements MyComponent {
}
What I did so far is to create two conditions like so:
imple1
public class FirstCondition implements Condition {
#Override
public boolean matches(ConditionContext arg0, AnnotatedTypeMetadata arg1) {
return staticVariable.contains("impCompf");
}
}
Same goes for imple2
and define a configuration class
#Configuration
public class MyConfiguration {
#Bean
#Conditional(FirstCondition .class)
#Primary
public MyComponent getComp1() {
return new ImpComp1();
}
public static String staticVariable= "impCompf";
and in My main controller:
#RequestMapping(value="api/{co}", method=RequestMethod.POST)
public ResponseEntity<Modelx> postSe(#PathVariable("co") String co) {
if(co.contains("impCompf"))
staticVariable = "impCompf";
else (co.contains("impComps"))
staticVariable = "impComps";
What I want: for every http request I want to load proper implementation
But however what I am getting is the implementation defined first in the static variable.
If is there another elegant and better way, i'd like to know about it.
I think there is some confusion here about the purpose of the conditions. These aren't being used at the time your requests arrive to autowire the candidate bean into your controller. These are being used when the application is started to configure the application context based on the environment and classpath etc...
There is no need for the conditional classes that you have created. This is defining the configuration of the beans when the context starts and not on a per request basis at runtime.
The use of the static variable is also problematic is a scenario with one or more concurrent requests or in a case where multiple threads may observe different values unless some other mechanism in the java memory model is being used (such as volatile or establishing a happens before relationship, e.g. with sychnronized)
There are a number of ways to do what you appear to be trying to achieve. Since ultimately, you appear to be using a path parameter supplied by a client to determine which service you want to invoke you could use a classic factory pattern to return the correct interface implementation based on the string input programmatically.
Alternatively you could create two distinct controller methods which are distinguished by a query parameter or endpoint name or path match etc. You could then have the appropriate service injected by a qualified bean name
Although perhaps generally recommended, you could also inject an application context instance and search the it looking for the relevant bean by name or class: https://brunozambiazi.wordpress.com/2016/01/16/getting-spring-beans-programmatically/ - although This is more cumbersome and you'd need to handle things like org.springframework.beans.factory.NoSuchBeanDefinitionException or casting in some cases - best avoided in favour of one of the other methods.

Spring data rest and business rules validation

I want to seek a best practice for applying business rules when working with spring data rest.
Lets consider following scenario:
I have a Customer and Order in #OneToMany relationship.
I have a business rule saying that Customer needs to have verified flag set to be able to make orders
So I need to make sure that whenever someone POSTs to /orders the Customer making the call is verified.
I'm considering using beforeSave Validators autowiring other service/repositories into the Validator and check whatever needs to be checked.
Is there better way of achieving the same?
There are several ways to solve this. As far as my knowledge goes:
Usage of spring security annotations like #PreAuthorize. The intended use of these annotations is however for security purposes and you are mentioning business rules. I would use these for user authorization rules Spring data rest security chapter
The use of validators as you mentioned yourself. Spring data rest Validators
Use spring data rest events Spring data rest events. You can create global event handlers, however here you need to determine the entity type. I would go with Annotated event handlers to perform business logic Spring data rest annotated event handler
So just for the sake of world piece I'm adding my solution. Went with #2.
The documentation is pretty clear on how to proceed so just sharing few tips which may save you time.
You need to assign validators manually, auto-discovery doesn't work
Manually spelling event type is error prone, some helper Enum could be handy.
Like:
/**
* "beforeSave" gets called on PATCH/PUT methods
* "beforeCreate" on POST
* "beforeDelete" on DELETE
*/
enum Event {
ON_CREATE("beforeCreate"), ON_UPDATE("beforeSave"),
ON_DELETE("beforeDelete");
private String name;
Event(String name) {
this.name = name;
}
}
...
private static void addValidatorForEvents(ValidatingRepositoryEventListener eventListener, Validator validator, Event... events) {
Arrays.asList(events).forEach(event -> eventListener.addValidator(event.name, validator));
}
One out of the box solution you can use to solve your Business rules related problems, is using Spring AOP. What you can do, is define an Annotation (say #X) and place that annotation on top of your POST call.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface X{}
Next what you need to do is, create an aspect, and run your custom validation logic in this aspect as follows,
#Aspect
#Component
public class CustomAspect {
//You can autowire beans here
#Around("#annotation(qualified name of X)")
public Object customMethod(ProceedingJoinPoint joinPoint) throws Throwable {
flag = customLogic();
if (flag){
return joinPoint.proceed(); //return if logic passes, otherwise
}else{
throw new BusinessRuleException("Business rule violated");
}
}
private boolean customLogic(){
//your custom logic goes here
}
}
And finally apply this annotation on top of any method in controller layer like:
#X
#RequestMapping(method = RequestMethod.POST, value = "do-something")
public void callSomething(HttpServletRequest request) throws Exception {
// your business logic goes here
}
Only thing to note above is that you need to pass HttpServletRequest request explicitly to your controller method in order to AOP aspect get the same context for manipulation of user session related attributes like session_id, etc.
Above solution will help you add business rules on top of your Business Logic and help you with all kinds of pre validations you want to build in your web application. It is a pretty handy application of Spring AOP. Do reach out in case of any

How to make Ninject return a mock object

I have a code that looks like this
public class EmployeeController : Controller
{
public ContextWrapper contextWrapper;
public EmployeeController (IContextWrapper wrapper)
{
contextWrapper = wrapper;
}
In my dependency resolver I have the binding for IContextWrapper
kernel.Bind<IContextWrapper>().To<ContextWrapper>();
The implementation of ContextWrapper has an object which is of type Linq DataContext.
public class ContextWrapper : IContextWrapper
{
public MyDataContext dataContext;
public ContextWrapper(MyDataContext context)
{
this.dataContext = context;
}
Now my action method in this controller looks like this
var empRepository = new EmployeeRepository(contextWrapper);
//do some tests with this repository.
some values = contextWrapper.datacontext.get some values from the database table
//do some tests with these values.
To be able to test this method
I should be able to provide some sort of mock database(not literally) or
make the contextWrapper.datacontext return mocked values or
I even thought of creating another implementation of the IContextWrapper that doesn't use a Linq DataContext object. And creating another constructor for this controller and pass that fake implementation. Also in my dependency resolver I would bind the fake object to the IContextWrapper. Although I do not know how to make Ninject
As a last resort test my method against a test database since it all boils down to this Linq DataContext object and it seems I cannot get rid of it past a certain level.
Problem is the more I read about it, more I get confused. I have tried to give as much detail as possible to explain my problem. If any one has a clear cut idea about how to get this, please suggest.

Why is my Spring 3 Validator Validating Everything on the Model?

I have a spring 3 controller with a validator for one of the methods. It insists on validating every object on the model. Would anyone be able to explain to me why it does this or if I'm doing something wrong?
According to the docs, 5.7.4.3 Configuring a JSR-303 Validator for use by Spring MVC (http://static.springsource.org/spring/docs/3.0.0.RC3/spring-framework-reference/html/ch05s07.html)
With JSR-303, a single javax.validation.Validator instance typically validates all model objects that declare validation constraints. To configure a JSR-303-backed Validator with Spring MVC, simply add a JSR-303 Provider, such as Hibernate Validator, to your classpath. Spring MVC will detect it and automatically enable JSR-303 support across all Controllers.
Example:
#Controller
public class WhaleController {
#Autowired
private Validator myValidator;
#Autowired
private WhaleService whaleService;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(this.myValidator);
}
#RequestMapping(value="/save-the-whales")
#Transactional
public void saveTheWhales(#Valid WhaleFormData formData, BindingResult errors, Model model) {
if (!errors.hasFieldErrors()) {
Whale whale = new Whale();
whale.setBreed( formData.getBreed() );
this.whaleService.saveWhale( whale );
model.addAttribute("whale", whale);
}
model.addAttribute("errors", errors.getFieldErrors());
}
}
When run it will complain that Whale is an invalid target for myValidator (which is set to validate WhaleFormData, and does so fine). Whale is a POJO with no validation constraints, annotation and no config anywhere. Through trial and error I've found that ANY object placed on the model will attempt to be validated and fail if the validator is not setup to handle it. Primitives are just fine.
Can anyone tell me why this is, point me to the appropriate documentation and/or tell me the best way to put something on the model without having it validated?
In the case above I would like to place "whale" on the model as it will now have a unique whaleId() that it received from my persistence layer.
Thanks!
I guess this behaviour is not covered in the documentation well.
The problem is caused by the following:
By default, #InitBinder-annotated method is called for each non-primitive model attribute, both incoming and outcoming (the purpose of calling it for outcoming attibutes is to allow you to register custom PropertyEditors, which are used by form tags when rendering a form).
DataBinder.setValidator() contains a defensive check that call Validator.supports() and throws an exception if false is returned. So, there is no attempt to perform a validation, just an early check.
The solution is to restrict the scope of #InitBinder to particular attribute:
#InitBinder("whaleFormData")
protected void initBinder(WebDataBinder binder) { ... }

Resources