spring getting bean using ApplicationContext.getBean() - spring

I want to get a bean of a class A that implements Class B,
public class AndroidDeviceRule implements DeviceRule {}
this is fine
return (DeviceRule) context.getBean(myBeanName, DeviceRule.class);
But, I would prefer something like
return (DeviceRule) context.getBean(mybeanName, Class<? extends DeviceRule>);
But I can't..
- Syntax error on token ",", ( expected after
this token
- Syntax error on token(s), misplaced
construct(s)

I will recommend Autowiring bean for this
#Autowired
DeviceRule deviceRule;
Much cleaner approach

What you can do is to define the actual DeviceRule subtype as a generic type at the method level. You can have this in two flavors:
// with an explicit type passed in (as in your example)
<T extends DeviceRule> T getSpringBean(String name, Class<T> type) {
return (T) applicationContext.getBean(name, type);
}
// with no explicit type; will return whatever the caller expects,
// obviously resulting in a ClassCastException if the cast fails.
<T extends DeviceRule> T getSpringBean(String name) {
return (T) applicationContext.getBean(name);
}

Related

How to link a Vaadin Grid with the result of Spring Mono WebClient data

This seems to be a missing part in the documentation of Vaadin...
I call an API to get data in my UI like this:
#Override
public URI getUri(String url, PageRequest page) {
return UriComponentsBuilder.fromUriString(url)
.queryParam("page", page.getPageNumber())
.queryParam("size", page.getPageSize())
.queryParam("sort", (page.getSort().isSorted() ? page.getSort() : ""))
.build()
.toUri();
}
#Override
public Mono<Page<SomeDto>> getDataByPage(PageRequest pageRequest) {
return webClient.get()
.uri(getUri(URL_API + "/page", pageRequest))
.retrieve()
.bodyToMono(new ParameterizedTypeReference<>() {
});
}
In the Vaadin documentation (https://vaadin.com/docs/v10/flow/binding-data/tutorial-flow-data-provider), I found an example with DataProvider.fromCallbacks but this expects streams and that doesn't feel like the correct approach as I need to block on the requests to get the streams...
DataProvider<SomeDto, Void> lazyProvider = DataProvider.fromCallbacks(
q -> service.getData(PageRequest.of(q.getOffset(), q.getLimit())).block().stream(),
q -> service.getDataCount().block().intValue()
);
When trying this implementation, I get the following error:
org.springframework.core.codec.CodecException: Type definition error: [simple type, class org.springframework.data.domain.Page]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.data.domain.Page` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 1]
grid.setItems(lazyProvider);
I don't have experience with vaadin, so i'll talk about the deserialization problem.
Jackson needs a Creator when deserializing. That's either:
the default no-arg constructor
another constructor annotated with #JsonCreator
static factory method annotated with #JsonCreator
If we take a look at spring's implementations of Page - PageImpl and GeoPage, they have neither of those. So you have two options:
Write your custom deserializer and register it with the ObjectMapper instance
The deserializer:
public class PageDeserializer<T> extends StdDeserializer<Page<T>> {
public PageDeserializer() {
super(Page.class);
}
#Override
public Page<T> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
//TODO implement for your case
return null;
}
}
And registration:
SimpleModule module = new SimpleModule();
module.addDeserializer(Page.class, new PageDeserializer<>());
objectMapper.registerModule(module);
Make your own classes extending PageImpl, PageRequest, etc. and annotate their constructors with #JsonCreator and arguments with #JsonProperty.
Your page:
public class MyPage<T> extends PageImpl<T> {
#JsonCreator
public MyPage(#JsonProperty("content_prop_from_json") List<T> content, #JsonProperty("pageable_obj_from_json") MyPageable pageable, #JsonProperty("total_from_json") long total) {
super(content, pageable, total);
}
}
Your pageable:
public class MyPageable extends PageRequest {
#JsonCreator
public MyPageable(#JsonProperty("page_from_json") int page, #JsonProperty("size_from_json") int size, #JsonProperty("sort_object_from_json") Sort sort) {
super(page, size, sort);
}
}
Depending on your needs for Sort object, you might need to create MySort as well, or you can remove it from constructor and supply unsorted sort, for example, to the super constructor. If you are deserializing from input manually you need to provide type parameters like this:
JavaType javaType = TypeFactory.defaultInstance().constructParametricType(MyPage.class, MyModel.class);
Page<MyModel> deserialized = objectMapper.readValue(pageString, javaType);
If the input is from request body, for example, just declaring the generic type in the variable is enough for object mapper to pick it up.
#PostMapping("/deserialize")
public ResponseEntity<String> deserialize(#RequestBody MyPage<MyModel> page) {
return ResponseEntity.ok("OK");
}
Personally i would go for the second option, even though you have to create more classes, it spares the tediousness of extracting properties and creating instances manually when writing deserializers.
There are two parts to this question.
The first one is about asynchronously loading data for a DataProvider in Vaadin. This isn't supported since Vaadin has prioritized the typical case with fetching data straight through JDBC. This means that you end up blocking a thread while the data is loading. Vaadin 23 will add support for doing that blocking on a separate thread instead of keeping the UI thread blocked, but it will still be blocking.
The other half of your problem doesn't seem to be directly related to Vaadin. The exception message says that the Jackson instance used by the REST client isn't configured to support creating instances of org.springframework.data.domain.Page. I don't have direct experience with this part of the problem, so I cannot give any advice on exactly how to fix it.

Dependency-inject "dynamically specified" beans based on annotation arguments

I have a use case where it would be extraordinarily nice to dynamically instantiate beans (using some kind of factory approach) based on annotation-arguments at the injection point. Specifically, I need to be able to specify a type-argument to the bean-creating factory.
A pretty relevant example would be a JSON deserializer that needs the type which it needs to deserialize to.
I envision either:
#Inject
#DeserializeQualifier(Car.class)
private Deserializer<Car> _carDeserializer;
#Inject
#DeserializeQualifier(Bus.class)
private Deserializer<Bus> _busDeserializer;
.. or simply, if it was possible to sniff the type from the generic type argument:
#Inject
private Deserializer<Car> _carDeserializer;
#Inject
private Deserializer<Bus> _busDeserializer;
The big point here is that I would not know beforehand which types was needed in the project, as this would be a generic tool that many projects would include. So you would annotate your #Configuration class with #EnableDeserializer and could then inject any type deserializer (The factory that makes these deserializers can handle any type, but to be able create one, it would need to know the desired type of the deserialized object - plain generics would not cut it, since Java ain't using reified generics).
So, I'd need to be able to inject into the spring context, or using any other Spring magic tricks, some kind of DeserializerFactory that takes the type argument.
Basically, I need to have Spring invoke the following method based based on either, as in the first example, the qualifier argument (or the entire DeserializeQualifier-instance for that matter), or as in the second example, the generic type argument:
DeserializerFactory {
<T> Deserializer<T> createDeserializer(Class<T> type) { ... }
}
You could create a BeanFactoryPostProcessor to set attributes annotated with a custom annotation. I've set up a small Spring Boot project to play around:
// Custom annotation
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectSomeClassHere {
Class value();
}
// Demo bean
#Component
public class SomeBean {
#InjectSomeClassHere(String.class)
private Class someValue;
public Class getInjectedClass() {
return someValue;
}
}
// The BeanFactoryPostProcessor
#Component
public class SomeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Arrays
.stream(beanFactory.getBeanDefinitionNames())
.filter(beanName -> hasAnnotatedField(beanFactory, beanName))
.forEach(beanName -> {
Object bean = beanFactory.getBean(beanName);
Stream.of(bean.getClass().getDeclaredFields()).forEach(field -> setFieldValue(bean, field));
});
}
private boolean hasAnnotatedField(ConfigurableListableBeanFactory beanFactory, String beanName) {
try {
String className = beanFactory.getBeanDefinition(beanName).getBeanClassName();
if (className == null) {
return false;
}
return Arrays.stream(Class.forName(className).getDeclaredFields())
.anyMatch(field -> field.isAnnotationPresent(InjectSomeClassHere.class));
} catch (ClassNotFoundException e) {
// Error handling here
return false;
}
}
private void setFieldValue(Object filteredBean, Field field) {
try {
// Note: field.isAccessible() is deprecated
if (!field.isAccessible()) {
field.setAccessible(true);
}
// Retrieve the value from the annotation and set the field
// In your case, you could call `createDeserializer(fieldValue);` and set the field using the return value.
// Note that you should change the type of `SomeBean#someValue` accordingly.
Class fieldValue = field.getAnnotation(InjectSomeClassHere.class).value();
field.set(filteredBean, fieldValue);
} catch (IllegalAccessException e) {
// Error handling here
e.printStackTrace();
}
}
}
// A small test to verify the outcome of the BeanFactoryPostProcessor
#RunWith(SpringRunner.class)
#SpringBootTest
public class SomeBeanTests {
#Autowired
private SomeBean someBean;
#Test
public void getInjectedClass_shouldHaveStringClassInjected() {
Assert.assertEquals(String.class, someBean.getInjectedClass());
}
}
Please note that this is a very naive implementation and requires further fine tuning. For instance, it scans all attributes in all spring components for the presence of an annotation.
Good luck with your project!

Conditionally create bean if Map of properties is not empty with SpEL

I have a class with a method that is periodically invoked with the #Scheduled annotation. The method does some bulk operations on a given set of properties.
If there are no properties set, I don't need the scheduled method invocation nor the instantiated class. Therefore, I've added this SpEL expression to check whether the properties are set:
#Service
#ConditionalOnExpression("#(T(java.util.Map)('${myproperties.people:{:}}')).size() > 0")
public class PeopleService { ... }
Example values in the application.yml could be:
myproperties:
people:
uuid1:
name: Mark
age: 32
uuid2:
name: Jeff
age: 36
Unfortunately I get this error message:
Caused by: org.springframework.expression.spel.SpelParseException: Expression [#(T(java.util.Map)('{:}')).size() > 0] #1: EL1043E: Unexpected token. Expected 'identifier' but was 'lparen(()'
Note that I came up with {:} for an empty map as default value here: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions-inline-maps
If I use this SpEL, I get the following error: "#(T(java.util.Map)(${myproperties.people:})).size() > 0"
Caused by: org.springframework.expression.spel.SpelParseException: Expression [#(T(java.util.Map)()).size() > 0] #1: EL1043E: Unexpected token. Expected 'identifier' but was 'lparen(()'
What is the correct way to accomplish this?
This annotation:
#ConditionalOnExpression("#(T(java.util.Map)('${myproperties.people:{:}}')).size() > 0")
Doesn't work because Spring read LITERALLY all your properties like that:
myproperties.people.uuid1.name=Mark
myproperties.people.uuid1.age=32
myproperties.people.uuid2.name=Jeff
myproperties.people.uuid2.age=36
If you check Spring Boot documentation, this page explains that "the condition matches if spring.example.values is present in the Environment but does not match if spring.example.values[0] is present.".
So for Spring, property "myproperties.people" just doesn't exist. On the same page, they explain that "it is better to use a custom condition for such cases."
A workaround is to define a property class (but it's not wasted, because you will use this class to parse your yaml as a map):
#ConfigurationProperties(prefix = "myproperties")
public class MyPeopleProperties {
private Map<String, String> people;
public Map<String, String> getPeople() {
return people;
}
public void setPeople(Map<String, String> people) {
this.people = people;
}
}
And on your main class:
...
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
...
#Service
#Conditional(PeopleService.PeopleServiceCondition.class)
public class PeopleService {
...
public static class PeopleServiceCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MyPeopleProperties config = Binder.get(context.getEnvironment())
.bind("myproperties", MyPeopleProperties.class).orElse(null);
return config != null && config.getPeople() != null && !config.getPeople().isEmpty();
}
}
}
So myproperties.people will be bound as map, then we check if this map exists on the bean declaration condition.
Circling back around to the SpEL implementation, you actually can do it entirely in SpEL but it's really ugly:
#ConditionalOnExpression(
"(" +
"T(org.springframework.boot.context.properties.bind.Binder)" +
".get(environment)" +
".bind('myproperties.people', T(java.util.Map))" +
".orElse(null)" +
"?.size()" +
"?: 0" +
") > 0"
)
This will work even for deeply-nested config subtrees - the simplistic binding to the Map class just creates a bunch of denormalized/flattened <String,String> entries with keys like uuid1.name. That means that if you are just testing for the existence of any sub-tree elements under the myproperties.people namespace, the above will work fine. It is, in effect, exactly what you probably would have meant when writing #ConditionalOnProperty("myproperties.people")

Bean property 'iUserGeneratorInterface' is not writable or has an invalid setter method

I have a project structure as follows
[1]: http://i.stack.imgur.com/T1jvh.png
I have a class and interface defined in create-user
UserGeneratorInterface
package com.credit.userGenerator;
public interface UserGeneratorInterface {
public String userIdGenerator();
}
RandomUserGenerator.java
public class RandomUserGenerator implements UserGeneratorInterface {
public static enum Mode {
ALPHA, ALPHANUMERIC, NUMERIC
}
public static String generateRandomString(int length, Mode mode) throws Exception {
code logic
}
public String userIdGenerator(){
code logic
}
}
I have defined application context in da-web and wan to get service of RandomUserGenerator.java
public class ApplicationSignManager implements IApplicationSignInterface {
public UserGeneratorInterface iUserGeneratorInterface;
public UserGeneratorInterface getiUserGeneratorInterface() {
return iUserGeneratorInterface;
}
public void setiUserGeneratorInterface(
UserGeneratorInterface iUserGeneratorInterface) {
this.iUserGeneratorInterface = iUserGeneratorInterface;
}
}
AppContext.xml
<bean id="userGenerator" class="com.credit.userGenerator.RandomUserGenerator" ></bean>
<bean id="appSign" class="com.*****.service.ApplicationSignManager">
<description>List of Dao in ApplicationLogin Manager</description>
<property name="userGeneratorInterface" ref="userGenerator"/>
</bean>
But I am getting the following exception
Bean property 'iUserGeneratorInterface' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
As noticed in the comments, use setIUserGeneratorInterface() and getIUserGeneratorInterface() with capital 'I'.
Use 'property name="iUserGeneratorInterface" ref="userGenerator"' - note the leading 'i'.
Note that it is always advisable to use well-established practices when naming your classes and fields. In your case naming 'UserGeneratorInterface' simply 'UserGenerator' and naming the field 'iUserGeneratorInterface' simply 'userGenerator' would make more sense. Note also that you have declared this field public (although this has nothing to do with the errors).
Your bean id="userGenerator"
Use this "userGenerator" like (public UserGeneratorInterface userGenerator;) as same as the variable name.
And create getter, setter for this variable
then try

JSF ManagedProperty not working for class

Sorry for my English. I want to set #ManagedProperty for class TaskBO, but it is not works. TaskMB class:
#ManagedBean(name="taskMB")
#RequestScoped
public class TaskMB implements Serializable {
#ManagedProperty(value="#{TaskBO}")
public TaskBO taskBO;
public TaskBO getTaskBO() {
return this.taskBO;
}
public void setTaskBO(TaskBO taskBO){
this.taskBO = taskBO;
}
//...
}
It prints the error:
javax.servlet.ServletException: Unable to set property taskBO for managed bean taskMB
javax.el.ELException: java.lang.IllegalArgumentException: Cannot convert com.otv.model.bo.TaskBO#6c80b8 of type class $Proxy135 to class com.otv.model.bo.TaskBO
But if I add interface ITaskBO, that it is works:
#ManagedProperty(value="#{TaskBO}")
public ITaskBO taskBO;
public ITaskBO getTaskBO() {
return this.taskBO;
}
public void setTaskBO(ITaskBO taskBO){
this.taskBO = taskBO;
}
Why not work #ManagedProperty with the class TaskBO?
Is best pratice wire interface instead of concrete class to prevent the problem you encountered.
Cannot convert com.otv.model.bo.TaskBO#6c80b8 of type class $Proxy135
to class com.otv.model.bo.TaskBO
Often Spring's managed object are proxied and a java proxy can be casted ONLY to interface and not to concrete class; the error above is generated because:
TaskBO object is managed by Spring and proxied to an object of type $Proxy135 (the real type of your object now is not really concrete class TaskBO but a proxy you can cast to ITaskBO, the $Proxy135)
you are trying to do some like public TaskBO taskBO = (TaskBO)$Proxy135; but cast a proxy to concrete class is impossible
The right way is to write public ITaskBO taskBO = (ITaskBO)$Proxy135; and this works because a proxy can be cast only to interface
Avoid - as much as possible - use of concrete class in favor of interface.
In addiction you can look here if you are mixing configuration how described in linked question.

Resources