Spring getBean with type validation - spring

I'm using the method ApplicationContext.getBean(String name, Class requiredType). The bean is of type util:set. My code looks like:
Set<String> mySet = context.getBean("myBean", Set.class);
I want to know is how to do something like this to avoid the type casting warning:
Set<String> mySet = context.getBean("myBean", Set<String>.class);
I'm not sure if it's possible to define the type of a class in this way. Am I dreaming or is there a way to do this?
Thanks.

Not really but there is a runtime workaround that at least removes the need for an #SuppressWarnings. You can make your class abstract and let Spring instrument your code:
public abstract class Test {
Set<String> getMyBean();
}
and then inject a lookup method in your XML config:
<bean class="Test">
<lookup-method name="myBean" bean="myBean" />
</bean>
It's not really statically checked but it fails-fast at runtime and you keep the ugly casting out of your code.

maybe this can be usefull to you:
Set<String> setBean= null;
Map<String, Set> beans = applicationContext.getBeansOfType(Set.class);
for (Map.Entry<String, Set> bean: beans.entrySet()) {
ParameterizedType thisType = (ParameterizedType) bean.getClass().getGenericSuperclass();
Class<?> parametrizedClass= thisType.getActualTypeArguments()[0];
if (parametrizedClass.isAssignableFrom(String)) {
setBean= (Set<String>) bean;
}
}
http://javahelp.redsaltillo.net

Related

Spring-boot ConditionalOnProperty with map-based properties

My spring-boot yaml properties look like this:
service:
mycomponent:
foo:
url: http://foo
bar:
url: http://bar
This results in the following properties being set in the Spring environment:
service.mycomponent.foo.url: http://foo
service.mycomponent.bar.url: http://bar
I'd like to define a 'mycomponent' bean if there are any properties that match service.mycomponent.[a-z]*.url. Is this possible using #ConditionalOnExpression or some other type of #Conditional?
I realize I can work around this by either adding a property such as service.mycomponent.enabled: true that could be used with #ConditionalOnProperty but I'd rather avoid that if possible.
Here's the solution I ended up taking:
Create a custom Condition which searches for any properties with a certain prefix. The RelaxedPropertyResolver has the convenient getSubProperties() method. Alternative options I found were cumbersome to iterate through the PropertySource instances.
public class MyComponentCondition extends SpringBootCondition {
#Override
public ConditionOutcome getMatchOutcome(final ConditionContext context,
final AnnotatedTypeMetadata metadata) {
final RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(context.getEnvironment());
final Map<String, Object> properties = resolver.getSubProperties("service.mycomponent.");
return new ConditionOutcome(!properties.isEmpty(), "My Component");
}
}
Use that condition when setting up the bean:
#Conditional(MyComponentCondition.class)
#Bean
public MyComponent myComponent() {
return new MyComponent();
}
I'm still curious if the same thing could be done with #ConditionalOnExpression directly.

Spring Boot property in #Preauthorize

I'm setting up a Spring Boot (v1.2.6) web project and using Spring Security (v3.2.8). I've found the #PreAuthorize annotation so handy, but I don't know if there's a way to read Boot's properties from the SpEL in the annotation. I'm trying to do it this way:
#PreAuthorize("mysecurity.permit")
With the property declared in application.yml:
mysecurity:
permit: true
But I'm getting
Failed to evaluate expression 'mysecurity.permit'
I've made an attempt with #mysecurity.permit and ${mysecurity.permit} too, with the same result. It seems possible to declare a method in a service and access it in #service.isMySecurityPermited() way, however I would be pleased to know if I'm able to access the property directly.
The values used in an annotation must be constants. They are evaluated at compile time, and while they may be retained for use at runtime they aren't re-evaluated. So you can use an expression that's evaluated by SpEL, or you can write a helper method that is referenced within the annotation value.
If you look at the OnExpressionCondition implementation, you will notice that it gets the value passed to the annotation, which in the case linked in your comment would be something like #ConditionalOnExpression("${server.host==localhost} or ${server.port==8080} ") The annotation simply gets the text value, it has no idea what the text represents, it just knows it's a string. It's in the processing of the annotation value within OnExpressionCondition that the String value takes meaning. They take the String value and pass it to a BeanExpressionResolver for resolution.
So, in your PreAuthorize solution, which based on http://forum.spring.io/forum/spring-projects/security/100708-spel-and-spring-security-3-accessing-bean-reference-in-preauthorize also passes it to an expression processor, you should be able to use spring's expression language to reference any bean property.
I'm not in a situation to test it currently, but from that thread it seems like you could do something like
#Component
public class MyBean {
#Value("${mysecurity.permit}")
private Boolean permit;
public boolean isPermitted() { return permit; }
#PreAuthorize( "#myBean.isPermitted()" )
public blah myMethod() {
// do stuff
}
}
This maybe a generic way to evaluate expressions which i want to share with you:
#Component("AuthorizationComponent")
public final class AuthorizationComponent {
private final static Logger logger = Logger.getLogger(AuthenticationUtils.class.getName());
private static SpelExpressionParser parser;
static {
parser = new SpelExpressionParser();
}
#Autowired
private Environment environment;
public boolean evaluateExpression(final String propertyKey) {
return checkExpression(environment.getProperty(propertyKey));
}
public static boolean checkExpression(String securityExpression) {
logger.info("Checking security expression [" + securityExpression + "]...");
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
Expression exp = parser.parseExpression(securityExpression);
SecurityExpressionRoot context = new CustomMethodSecurityExpressionRoot(authentication);
boolean result = exp.getValue(context, Boolean.class);
logger.info("Check result: " + result);
return result;
}
}
And in yaml config file you can configure the path and authorization expression, something like that:
preAuthorize:
whatever:
post: hasRole('MY_ROLE') OR hasAuthority('MY_AUTHORITY')
Then you could use it like that over your method:
#PreAuthorize("#AuthorizationComponent.evaluateExpression('preAuthorize.whatevert.post')")
#RequestMapping(value = "", method = RequestMethod.POST)
public ResponseEntity<Void> addQuestion(#Valid #RequestBody BodyRestDTO bodyRestDTO){
//Code implementation
return new ResponseEntity<Void>(HttpStatus.CREATED);
}
This should work:
#Value("${mysecurity.permit}")
private Boolean permit;
Then use:
#PreAuthorize(permit)
But you need to properly set configuration file, to allow Spring access it. Read here:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
UPDATE:
Did you configure bean for a property placeholder?
For example:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:com/foo/app.properties</value>
</list>
</property>
</bean>

Inject properties via annotation

I am new for spring security. I've seen many posts on how to inject values via annotation from external property file. I've tried many ways, but I always end up with java.lang.IllegalArgumentException: Could not resolve placeholder 'val.id' exception.
Can you provide me some tips how to handle this exception please?
My java class is the following one:
#Controller
public class Employee {
#Value("${val.id}")
public String valId;
public String getValId() {
return valId;
}
public void setValId(String valId) {
this.valId = valId;
}
My property file is called val.properties which is located under WEB-INF, and its content is
val.id=xyz
I put the following in my main context bean.
<context:property-placeholder location="/WEB-INF/*.properties" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/val.properties"/>
A continuous question:
The injecting values from properties file to annotated beans works fine as I accepted the answer above. However, I cannot able to inject it to #PreAuthorize(...) annotation by following the same procedure.
Assume I want to secure a method called 'update'. This method is allowed if and only if valId is equal to empId. values of valId and empId are initialized in the val.properties file.
my java bean is:
public class Employee {
public String valId;
public String empId;
public String getValId() {
return valId;
}
public void setValId(String valId) {
this.valId = valId;
}
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
}
my property file contains:
val.id=nn
emp.id=nn
I have the place holder configuration in my main context file:
<context:property-placeholder location="/WEB-INF/*.properties" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertiesFactoryBean"
p:location="/WEB-INF/val.properties"/>
My PreAuthorize annotation (method security) is:
#PreAuthorize("(#{valProp['val.id']} == #{valProp['emp.id']})")
public boolean update(){
//if accessable
return true;
}
But the expression #{valProp['val.id']} == #{valProp['emp.id']} is not evaluated.
Did I do any mistake to inject values? It was worked when I annotate member variables, but it doesn't work here. Any idea please? Thanks in advance.
try to consider the following
1). change your annotation to:
#Value("#{valProp['val.id']}")
2). Replace PropertyPlaceholderConfigurer by PropertiesFactoryBean.
Hope this will resolve the exception.
The reason why the exception is thrown is, because the property placeholder by default throws an exception when a values cannot be resolved.
Furthermore you have two property placeholders, via which probably not all values can be resolved.
You can change this behaviour via setting the ignore-unresolvable property:
<context:property-placeholder location="/WEB-INF/*.properties" ignore-unresolvable="true" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/val.properties" p:ignoreUnresolvablePlaceholders="true" />
Note however that b< turning off this feature typos in a property file will not be detected.

Spring: Using #Qualifier with Property Placeholder

I am trying to use a property placeholder as the attribute for #Qualifier, as follows:
#Autowired
#Qualifier("${beanName}")
private MyBean myBean;
However, this does not work, even though the property placeholder is available as a String value:
#Value("${beanName}")
private String beanName;
What would be a good workaround here, thanks very much.
I encountered exactly same issue. Just use Resource
#Resource(name="${beanName}")
private MyBean myBean;
I can't leave a comment so this is my answer:
As Adam B said maybe you can use spring profiles to achieve the result you are aiming to (wich result are you aiming?).
Another thing you can do is:
configure a map (using the spring util namespace) in your xml context configuration like this:
<util:map id="mapId" key-type="java.lang.String" value-type="com.xxx.interface-or-superclass">
<beans:entry key="${property.bean.name.1}" value-ref="bean1-defined-elsewehere"/>
<beans:entry key="${property.bean.name.2}" value-ref="bean2-defined-elsewehere"/>
<beans:entry key="${property.bean.name.3}" value-ref="bean3-defined-elsewehere"/>
</util:map>
then you can load this map in a bean called eg. "com.xxx.BeanSelector"
#Value("#{mapId}")
private Map<String, com.xxx.interface-or-superclass> myMap;
and add to this bean a methos like this:
public interface-or-superclass getBean(String beanName){
return myMap.get(beanName);
}
ok now you can have your final class similar to this:
#Autowired
private BeanSelector beanSelector;
#Value("${property.name.the.bean.you.want.to.use}")
private String beanName;
private interface-or-superclass myBean;
then you can istantiate myBean (maybe inside the method afterPropertiesSet() if you are implementing the interface InitializingBean)
in this way:
myBean = beanSelector.getBean(beanName);
// then check ifthe bean is not null or something like that
Ok it's a little messy and maybe you can act in a different way based on what you want achieve, but it's a workaround.
Just a try (don't really known the problem to solve) . You can use fixed bean name as usual (autowire with placeholder is not supported) but you can load different bean implementation from different xml based on property value.
.
Else think about a solution based on bean alias.
My 2 cents

Spring container - annotation for null argument

In my application that uses Spring container I created my own annotation, and I wanted at runtime get Class objects of classes that are annotated with my annotation. For this I wanted to utilize Spring container.
In my .xml configuration file I put
<context:component-scan base-package="some.package" >
<context:include-filter type="annotation" expression="some.package.Question" />
</context:component-scan>
so the classes that are annotated with my Question annotation are detected by Spring. Problem is that those classes don't have no parameter constructor, so now I have 2 options:
Define no-parameter constructor in those classes
Define the beans in .xml and use constructor-arg
but is it possible to annotate constructor arguments with some annotation, so Spring will know that it needs to pass null value during creation of a bean?
Also those beans will have prototype scope, and from the point of view of an application the contents of an constructor arguments are not known during the creation of a bean.
EDIT:
I had to use #Value("#{null}") for annotation constructor arguments
I think your first suggestion of using a no-arg constructor sounds cleaner - the reason is that the object created is, from your perspective, being considered properly initialized even though the instance variables have null values - this can be indicated by having a default constructor
If it cannot be changed, your approach of using #Value("#{null}") also works, I was able to test out in a test case:
#MyAnnotation
public class Component1 {
private String message;
#Autowired
public Component1(#Value("#{null}") String message){
this.message = message;
}
public String sayHello(){
return this.message;
}
}
This may not be what you're looking for, but if you want to re-use Spring's classpath scanner and wrap it in your own implementation, you can use the following;
Class annotation = [your class here ];
String offsetPath = [your path here ];
// Scan a classpath for a given annotation class
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
// MZ: Supply the include filter, to filter on an annotation class
scanner.addIncludeFilter(new AnnotationTypeFilter(annotation));
for (BeanDefinition bd : scanner.findCandidateComponents(offsetPath))
{
String name = bd.getBeanClassName();
try
{
Class classWithAnnotation = Class.forName(name);
}
catch (Exception e)
{
//Logger.fatal("Unable to build sessionfactory, loading of class failed: " + e.getMessage(), e);
return null;
}

Resources