Spring MVC ignores configured PropertyEditor and uses constructor instead - java

Using Spring 3.1 and given this kind of thing:
class Thing {
public Thing() {}
public Thing(String someProperty) {}
}
class ThingEditor extends PropertyEditorSupport{
#Override
public void setAsText(String text) {
if (text != null) {
Thing thing = new Thing(text); // or by using a setter method
setValue(thing);
}
}
}
class SomeController {
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Thing.class, new ThingEditor());
}
}
I found that the registered property editor was not being called unless I removed the constructor that takes a String in Thing - is this right?
Why is it doing this and ignoring the registered editor and how can I make it stop doing this?

By introducing your own constructor, you disable the default constructor generated by compiler. Default constructor is probably required by the framework in order to be able to instantiate your Thing. If you really need your own constructor, you could also provide a version without any parameters for framework's use.

Lock the property name when you register the PropertyEditorSupport:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Thing.class, "someProperty", new ThingEditor());
}

Related

Guice linked binding versus #Provides method

I have this problem.
Consider I have classes definitions like:
public interface ABCInterface
{
}
And its implementation:
public class ABCImpl
{
#Inject
private XYZ xyz;
}
When the Guice Config is like this:
public class MasterConfig extends AbstractModule
{
#Override
protected void configure()
{
// TODO Auto-generated method stub
bind(ABCInterface.class)
.to(ABCImpl.class);
}
}
And run it, then everything works fine and XYZ gets injected into it.
But when I use provider methods like this:
public class MasterConfig extends AbstractModule {
#Override
protected void configure() {
// TODO Auto-generated method stub
}
#Provides
public ABCInterface abc() {
return new ABCImpl();
}
}
Then, in this case, I get a null pointer exception when I try to use the injected XYZ, because that object remains null. I am suspecting, this is beacuse, I am returning a new object of ABCImpl and hence Guice is not able to build a dependency graph. Please correct me if I am wrong here?
Can anyone suggest, how to write the Provider method, so that everything gets injected properly like it does when I mention in the configure method.
Indeed, when you write new ABCImpl(), Guice doesn't get a chance to inject its dependencies. You can do this:
#Provides
ABCInterface abc(ABCImpl impl) {
return impl;
}
but you might as well just write bind(ABCInterface.class).to(ABCImpl.class); in that case, unless your provider method has some extra logic.

Guice inject based on annotation value

I would like to use goolge/guice inject a value based on a class i provide with the annotation.
AutoConfig annotation
#BindingAnnotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.PARAMETER, ElementType.FIELD })
public #interface AutoConfig {
// default null not possible
Class<? extends Provider<? extends ConfigLoader<?>>> provider() default XMLAutoConfigProvider.class;
}
This is my annotation which allows configuring the type of config, that should be used for the annotated fields.
Usecase:
#AutoConfig()
ConfigLoader<?> defaultConfig;
#AutoConfig(provider = JsonConfigProvider)
ConfigLoader<?> jsonConfig;
I want to have two configs, one default/xml one and a json one. They will probably never occur in the same class at the same time. But i don't know when the one or the other is used. I used the approach with a class because they are provided by some dependencies/libs and this annotation will be used for some (plugable) submodules.
MyGuiceModule
public class MyGuiceModule extends AbstractModule {
#Override
protected void configure() {
bind(new TypeLiteral<ConfigLoader<?>>() {})
.annotatedWith(AutoConfig.class)
.toProvider(autoConfig.provider());
}
}
This the critical part, i just cannot imagine how to implement it.
So basically i just want to use the provider class specified in the annotation.
Its not necessary to use the provider class here too. Because autoConfig.provider().newInstance() is basically all i need. (I need to use a setter on the new instance but thats all i want to do at this place)
To sum it up all i really want to do is push the annotation (or its values to the provider) either using the get(AutoConfig autoConfig) or in the constructor.
Currently i only use the constructor to inject the configFile value i want to set on the newly generated config instance.
If you know that #AutoConfig(provider = JsonConfigProvider) ConfigLoader<?> jsonConfig is going to return you exactly the results of jsonConfigProvider.get(), and JsonConfigProvider obviously has a public parameterless constructor for newInstance to work, why wouldn't you just ask for a JsonConfigProvider in the first place?
Fundamentally Guice is just a Map<Key, Provider> with fancy wrapping. The bad news is that this makes variable bindings like "bind Foo<T> for all T" impossible to express concisely, and that includes your "bind #Annotation(T) Foo for all T". The good news is that you still have two options.
Bind each provider separately
Though you can't inspect annotations during provision (or tell Guice to do so for you), Guice will compare annotations using their equals methods if you bind an annotation instance rather than an annotation class (the way you would with Names.named("some-name")). This means that you can bind a ConfigLoader<?> with each expected annotation in a Module. Of course, this also means you'll have to have a list of possible ConfigLoader Providers available at configuration time, but they have to be compile-time constants anyway if you're using them as annotation parameters.
This solution works with constructor injection as well, but for fields you'll need both #Inject and #AutoConfig(...), and AutoConfig will need to keep its #BindingAnnotation meta-annotation.
To do this, you're going to have to write an implementation of your annotation, the way Guice does with NamedImpl. Note that the implementations of equals and hashCode must match the ones Java provides in java.lang.Annotation. Then it's just a matter of (redundantly) binding like this:
for(Class<ConfigLoader<?>> clazz : loaders) {
bind(ConfigLoader.class).annotatedWith(new AutoConfigImpl(clazz))
.toProvider(clazz);
}
The definition of equals is up to you, which means you can (and should) bind #AutoConfig(ConfigEnum.JSON) and keep the Guice bindings in your modules rather than specifying your requested implementation all over your codebase.
Use custom injections
You can also use custom injections to search your injected types for custom annotations like #AutoConfig. At this point, you'd be using Guice as a platform to interpret #AutoConfig instead of #Inject, which means that constructor injection won't work but that you can control your injection based on the injected instance, field name, field annotation, annotation parameters, or any combination thereof. If you choose this style, you can drop #BindingAnnotation from AutoConfig.
Use the example in the wiki article linked above as your template, but at minimum you'll need to:
Use bindListener on Binder or AbstractModule to match types that need this custom injection.
In the TypeListener you bind, search injected types for #AutoConfig-annotated fields, and if they have any matching methods then bind those matching methods to a MembersInjector or InjectionListener. You'll probably want to tease the class literal out of the annotation instance here, and pass in the Field and Class as constructor arguments to the MembersInjector/InjectionListener.
In the MembersInjector or InjectionListener you write, instantiate the provider and set the field to the instance the provider provides.
This is a very powerful feature, which would futher allow you to--for instance--automatically provide the configuration based on which instance you're injecting into or based on the name of the field. However, use it carefully and document it heavily, because it may be counter-intuitive to your coworkers that Guice is providing for an annotation other than #Inject. Also bear in mind that this won't work for constructor injection, so refactoring from field injection to constructor injection will cause Guice to complain that it's missing a required binding to instantiate the class.
I had a similar problem. I wanted to use a custom annotation that receives a enum param to choose the implementation. After a lot of research, debug and testing, I came to the following solution:
//enum to define authentication types
public enum AuthType {
Ldap, Saml
}
//custom annotation to be used in injection
#Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
#BindingAnnotation
public #interface Auth {
AuthType value();
}
//defintion of authenticator
public interface Authenticator {
public void doSomehting();
}
//Authenticator implementations
public class LdapAuthenticator implements Authenticator {
#Override
public void doSomehting() {
// doing ldap stuff
}
}
public class SamlAuthenticator implements Authenticator {
#Override
public void doSomehting() {
// doing saml stuff
}
}
public class MyModule extends AbstractModule {
// annotate fields to bind to implementations
private #Auth(AuthType.Ldap) Authenticator ldap;
private #Auth(AuthType.Saml) Authenticator saml;
#Override
protected void configure() {
//bind the implementation to the annotation from field
bindAnnotated("ldap", LdapAuthenticator.class);
bindAnnotated("saml", SamlAuthenticator.class);
}
private void bindAnnotated(String fieldName, Class<? extends Authenticator> implementation) {
try {
//get the annotation from fields, then bind it to implementation
Annotation ann = MyModule.class.getDeclaredField(fieldName).getAnnotation(Auth.class);
bind(Authenticator.class).annotatedWith(ann).to(implementation);
} catch (NoSuchFieldException | SecurityException e) {
throw new RuntimeException(e);
}
}
}
//usage: add #Auth(<AuthType>) to the dependency
public class ClientClass {
private Authenticator authenticator;
#Inject
public ClientClass(#Auth(AuthType.Ldap) Authenticator authenticator) {
this.authenticator = authenticator;
}
}
Check the documentation of Binder
I tested the Jeff Bowman solution, but it apparently works only binding to providers
As a BindingAnnotations#binding-annotations-with-attributes states equals() and hashCode() should be properly implemented. So given that there is MyAnnotation
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER})
public #interface MyAnnotation {
SomeEnum value() default SomeEnum.A;
}
which is used to specify SomeInterface implementation(SomeDefault and SomeOther), SomeModule class could look like
public class SomeModule extends AbstractModule {
#Override
protected void configure() {
bind(Key.get(SomeInterface.class, createAnnotationClass(A))).to(SomeDefault.class);
// more common binding expresion
bind(SomeInterface.class).annotatedWith(createAnnotationClass(B)).to(SomeDefault.class);
}
private Annotation createAnnotationClass(SomeEnum someEnum) {
return new MyAnnotation() {
#Override
public SomeEnum value() {
return someEnum;
}
#Override
public Class<? extends Annotation> annotationType() {
return MyAnnotation.class;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyAnnotationCl myAnnoCl = (MyAnnotationCl) o;
return A == myAnnoCl.getValue();
}
#Override
public int hashCode() {
// from java annotation documentation
return (127 * "value".hashCode()) ^ value().hashCode();
}
};
}
}
Then annotation could be used as follows:
public class DoSomethingWithSomething {
private final SomeInterface someImplementation;
#Inject
public DoSomethingWithSomething(
#MyAnnotation SomeInterface someDefault
// #MyAnnotation(A) SomeInterface someDefault
// #MyAnnotation(B) SomeInterface someOther
) {
this.someImplementation = someDefault;
}
}

Custom #RequestParam type handler

Simple and short question: Is there a way to create a handler for custom #RequestParam types in Spring MVC?
I know I can register custom WebArgumentResolvers but then I cannot bind these to parameters. Let me describe my use case:
Consider I have defined a Model Class Account:
public class Account {
private int id;
private String name;
private String email;
}
My request handling method looks as follows:
#RequestMapping("/mycontroller")
public void test(Account account1, Account account2) {
//...
}
If I make a request mydomain.com/mycontroller?account1=23&account2=12 I would like to automatically load the Account objects from the database and return an error if they dont exist.
Yes, you should just register a custom property editor:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(CustomType.class,
new CustomTypePropertyEditor());
}
Update: Since you need to access the DAO, you need the property editor as a spring bean. Something like:
#Component
public class AccountPropertyEditor extends PropertyEditorSupport {
#Inject
private AccountDAO accountDao;
#Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(accountDao.getById(Integer.parseInt(text)));
}
#Override
public String getAsText() {
return String.valueOf(((Account) getValue()).getId());
}
}
And then, when registering the editor, get the editor via injection rather than instantiating it.

Is there a way to map a Spring 3.0 MVC #RequestParam directly to a Java Bean?

Can this somehow work? do I have to use #InitBinder somehow?
public String myActionHandler(ModelMap model, #RequestParam MyPojoBean myBean){
...
}
I'm sure I've seen this somewhere,
but I'm not sure where. Is
there a simple code example for this?
If the above is possible, how
can I catch the exception if the
request doesen't match the Bean?
You need to register a custom editor in initBinder:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(MyPojoBean.class, new MyPojoEditor());
}
class MyPojoEditor extends java.beans.PropertyEditorSupport {
#Override public String getAsText () {...}
#Override public void setAsText (String s) {...}
}
You can do this using #InitBinder (see #Abdullah's answer), which is best if you only need to do this for a single class, or using a custom WebArgumentResolver (see this other question), which is more general.

How do I register a custom type converter in Spring?

I need to pass a UUID instance via http request parameter. Spring needs a custom type converter (from String) to be registered. How do I register one?
Please see chapter 5 of the spring reference manual here: 5.4.2.1. Registering additional custom PropertyEditors
I have an MVC controller with RequestMapping annotations. One method has a parameter of type UUID.
Thanks toolkit, after reading about WebDataBinder, I figured that I need a method like this in my controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(UUID.class, new UUIDEditor());
}
UUIDEditor simply extends PropertyEditorSupport and overrides getAsText() and setAsText().
Worked for me nicely.
In extenstion to the previous example.
Controller class
#Controller
#RequestMapping("/showuuid.html")
public class ShowUUIDController
{
#InitBinder
public void initBinder(WebDataBinder binder)
{
binder.registerCustomEditor(UUID.class, new UUIDEditor());
}
public String showuuidHandler (#RequestParam("id") UUID id, Model model)
{
model.addAttribute ("id", id) ;
return "showuuid" ;
}
}
Property de-munger
class UUIDEditor extends java.beans.PropertyEditorSupport
{
#Override
public String getAsText ()
{
UUID u = (UUID) getValue () ;
return u.toString () ;
}
#Override
public void setAsText (String s)
{
setValue (UUID.fromString (s)) ;
}
}
Not sure what you are asking?
Spring comes with a CustomEditorConfigurer to supply custom String <-> Object converters.
To use this, just add the CustomEditorConfigurer as bean to your config, and add the custom converters. However, these converters are typically used when converting string attributes in the config file into real objects.
If you are using Spring MVC, then take a look at the section on annotated MVC
Specifically, have a look at the #RequestParam and the #ModelAttribute annotations?
Hope this helps?

Categories

Resources