How to set annotation property/attribute programmatically? - java

So I need to set the timeout parameter for #Transactional annotation. This property will come from a property file which I'm not able to do since I'm encountering "The value for annotation attribute Transactional.timeout must be a constant expression". Something like this
#Value("${mytimeout}")
private int myTimeout;
#Transactional(timeout=myTimeout)
public void myMethod(){
}
The only time the timeout attribute can be set by a variable is when a variable is final.
So, I was thinking if it is possible to set the timeout property programmatically while using the #Transaction annotation. Or any other way I can set this attribute Thanks!

If you need the same timeout for all transactions, you can configure it as defaultTimeout in your transaction manager
Otherwise, you may try to play with custom AnnotationTransactionAttributeSource and TransactionAnnotationParser, though you'll need to replace <tx:annotation-drivern> with manual definition of corresponding beans in order to configure a custom attribute source.
Then you can create a custom annotation and make TransactionAnnotationParser generate TransactionDefinition with custom timeout when it sees your annotation.
Otherwise, the easiest way to solve this problem is to give up using #Transactional and use TransactionTemplate instead.

Related

Postgres Hibernate set session variables for row level security

I am having trouble finding information about this issue I am running into. I am interested in implementing row level security on my Postgres db and I am looking for a way to be able to set postgres session variables automatically through some form of an interceptor. Now, I know that with hibernate you are able to do row-level-security using #Filter and #FilterDef, however I would like to additionally set policies on my DB.
A very simple way of doing this would be to execute the SQL statement SET variable=value prior to every query, though I have not been able to find any information on this.
This is being used on a spring-boot application and every request is expected to will have access to a request-specific value of the variable.
Since your application uses spring, you could try accomplishing this in one of a few ways:
Spring AOP
In this approach, you write an advice that you ask spring to apply to specific methods. If your methods use the #Transactional annotation, you could have the advice be applied to those immediately after the transaction has started.
Extended TransactionManager Implementation
Lets assume your transaction is using JpaTransactionManager.
public class SecurityPolicyInjectingJpaTransactionManager extends JpaTransactionManager {
#Autowired
private EntityManager entityManager;
// constructors
#Override
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
super.prepareSynchronization(status, definition);
if (status.isNewTransaction()) {
// Use entityManager to execute your database policy param/values
// I would suggest you also register an after-completion callback synchronization
// This after-completion would clear all the policy param/values
// regardless of whether the transaction succeeded or failed
// since this happens just before it gets returned to the connection pool
}
}
}
Now simply configure your JPA environment to use your custom JpaTransactionManager class.
There are likely others, but these are the two that come to mind that I've explored.

Setting the revision date manually with Hibernate Envers

As far as I know, Hibernate Envers stores a revision when you create, change or delete an object annotated with #Audited.
Envers sets automatically the revision date to the current time. Is it possibile to set this time manually?
I'd need this to handle a temporal collection where data has valid time, which I'd need to set manually.
You can, but it may not seem intuitive at first.
When Envers creates its revision-entity instance, several things happen.
The #RevisionTimestamp annotated property is set with the current time.
The optional RevisionListener is called and supplied the revision-entity instance.
You can specify a RevisionListener in two ways and this really depends on whether or not your currently supplying a custom revision-entity instance or using the instance Envers resolves based on your setup.
Supplying Custom Revision Entity
In this situation, you can specifying your RevisionListener by setting it on the #RevisionEntity class annotation on the entity class.
#RevisionEntity(YourCustomRevisionListener.class)
public class CustomRevisionEntity {
...
}
Supplying RevisionListener via configuration
In this situation, you'll want add an additional bootstrap configuration property for Hibernate, either via your hibernate.properties file or in your code where you explicitly set the hibernate configuration properties:
org.hibernate.envers.revision_listener=com.company.envers.YourCustomRevisionListener
Regardless of which approach you take, you'll then implement the listener's contract and explicitly set the timestamp value based on whatever rules your application needs:
public class YourCustomRevisionListener implements RevisionListener {
#Override
public void newRevision(Object revisionEntity) {
// I am going to assume here you're using a custom revision entity.
// If you are not, you'll need to cast it to the appropriate class implementation.
final CustomRevisionEntity revisionEntityImpl = (CustomRevisionEntity) revisionEntity;
revisionEntityImpl.setTimestamp( resolveValidTimestampValue() );
}
private long resolveValidTimestampValue() {
// implement your logic here.
}
}
There are a couple caveats here. If you need to resolve the value from some bean in your application space, you'll need to determine which of the following applies to you:
Using Hibernate Envers version prior to 5.3
In this case you'll have to use the legacy approach of ThreadLocal variables to pass application scope instances/values to access those inside the listener.
Using Hibernate Envers 5.3 or later with CDI
In this case you can simply inject the CDI bean using CDI's injection since we added support to automatically resolve CDI beans when we create the listener instance.
Using Hibernate Envers 5.3 or later with Spring 5.1+
You can inject spring beans directly into the listener using Spring's injection annotations just like the listener were a spring-bean.
Using Hibernate Envers 5.3 or later with Spring prior to 5.1
In this case, you'll need to use the legacy approach of ThreadLocal variables since Spring Framework didn't add support for injecting beans into Hibernate beans until 5.1.

Spring boot properties to be loaded at initialization and respect all and control #Aspect based on the value from property file

We are loading properties from an external file using #PropertySources. Now I want to enable/disable #Aspect based on a property. I tried using #ConditionalOnExpression which didn't work. I tried the same by creating a bean of propertyplaceholderconfig. Even in the same case, it didn't work. Then I tried #profile which also didn't work initially.
What I Figured out is that these variables are not initialized at the starting when propertysource or propertyplaceholder bean is used at startup. Some variables are always ignored like (logging.file). But #Value works fine. In order to set these variables, I've to pass them as JVM parameters.
So my questions are:
1. How can I make spring to always read specified property files at startup and respect all of them?
2. Which is the best way to enable/disable #Aspect. Using #profile or #ConditionalOnExpression or something else?
Currently, we are setting logging.file in the main method since this also behaves the same way. But you guys know that it's not the proper way as I may end up adding the properties one by one like this. I want to put all the properties into external files such that spring reads those files and sets its properties.
Our properties structure:
common.properties #This has all common properties
service.properties #Property specific to a service. This will also contain existing property from common.properties which will be overridden.
I understand that I can use profiles. But, we want to keep the properties outside such you need to restart service if you are changing the properties. I also don't want to pass the variables as JVM parameters then I've to pass most of the variables in this way. Passing -Dspring.config.location is also difficult as common.properties and service.properties are used and 'service.properties' filename varies for each service.
sample codes:
Mainclass:
#PropertySources({
#PropertySource(value = "file:${property_path}/common.properties", ignoreResourceNotFound = false),
#PropertySource(value = "file:${property_path}/service1.properties", ignoreResourceNotFound = true) })
public class MainClass {
static String logDirectory = ApplicationContext.getGlobalProperty("logging.file");
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MainClass.class);
Properties properties = new Properties();
properties.put("logging.file", logDirectory);
springApplication.setDefaultProperties(properties);
springApplication.run(args);
}
}
Application Context:
#Configuration
#EnableAutoConfiguration
public class ApplicationContext implements EnvironmentAware {
private static Environment environment;
#Override
public void setEnvironment(Environment environment) {
ApplicationContext.environment = environment;
}
public static String getGlobalProperty(String propertyName) {
return environment.getProperty(propertyName);
}
}
Here you can see any way I've used environment to get property. Is there any way to set the property using the environment such that while spring boot initialization itself the properties are populated?
We can also implement ApplicationContextInitializer and override initialize method to read properties. But how can I make it read 2 property files and override the duplicate property with the latest value? Reference(I'm not sure how to implement my requirements in this way.). Even in this case doesn't sound like you are trying to kill a mosquito with a hammer?
Current working Solution:
#Aspect
#Profile("!production")
#Configuration
public class ControllerAspect {
#pointcut(....)
} //Here also I've to pass spring.profiles.active as JVM params.
//setting the value in common.properties or service1.properties is not working.
I'm a newbie to spring boot so please let me know for additional clarifications.
It seems Spring by default loads some properties at initialization and unless until you specifically write logic to overwrite them (like the one I wrote in MainClass.java) there is no option to override those. Some of these include (logging.file, key used in #ConditionalonExpression).
Some tricks with their own challenges:
Specify the properties in application.properties in your classpath. The variables loaded at the earlier stages are always read from this file. challenge: I've tight coupled all my properties into the jar and in order to change the values I've to recompile and relaunch the Jar.
Use profiles and define application.properties as application-profile.properties. challenge: I've to create so many profiles and still the previous challenge exists.
Pass the property value as JVM parameter as -Dproperty.key=value. challenge:seriously? How many properties am I supposed to send as JVM parameter?
Implement ApplicationContextInitialize and override initialize method.challenge:Overriding Spring's default behaviour is not recommended as well as isn't it an overkill to use this just for reading property file?
Solution:
Use -Dspring.config.location to specify the property files. In this case, always spring reads the properties only from the specified location(s). You can provide multiple property files as well. Refer this for much more details. It seems if you give property locations as Directories spring loads them in reverse order. But if you specify files it follows the order specified.
Note: All these can be combined together. To know about precedence refer this.

Why #Required Annotation using JavaConfig doesn't throw an error if required value is not set [duplicate]

I'm pretty new to the Spring Framework and I got problems to understand the #Required annotation in combination with a Java configured application.
Here is an example.
Config-File
#Configuration
public class AppConfig {
#Bean
public Movie movieA() {
return new Movie();
}
#Bean
public MovieHolder holder() {
return new MovieHolder();
}
}
MovieHolder.java
public class MovieHolder {
private Movie movie;
public Movie getMovie() {
return movie;
}
#Required
public void setMovie(Movie movie) {
this.movie = movie;
}
}
Context initialization
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MovieHolder holder = (MovieHolder) context.getBean("holder");
System.out.println("movie: " + holder.getMovie());
As far as I understood the documentation of the #Required annotation, there should rise an exception, because movie isn't set directly or by autowiring. Instead is the output movie: null.
What am I doing wrong? Or isn't this the correct use of the #Required annotation?
Setting the required properties in the beans that you are instantiating is your own responsibility. The BeanPostProcessor that processes the bean-definitions in the classes annotated with #Configuration is called ConfigurationClassPostProcessor. The BeanPostProcessor that processes your #Required annotation defaults to RequiredAnnotationBeanPostProcessor, which is registered by default when you use context:annotation-config and context:component-scan in your configuration. If you are not using these two tags, you can even register your own RequiredAnnotationBeanPostProcessor as a bean.
Now, the default implementation of the RequiredAnnotationBeanPostProcessor has a method called boolean shouldSkip(..) that checks for a boolean attribute named SKIP_REQUIRED_CHECK_ATTRIBUTE. The value of this attribute is checked for each bean during the post-processing by the RequiredAnnotationBeanPostProcessor. If it returns false, the #Required constraint is enforced, otherwise it is not.
Now, the ConfigurationClassPostProcessor set the value of this attribute to true while creating the bean definitions from the #Configuration classes (I guess for the reason that if you are defining a bean, you should ensure that it has the required properties). Hence, the #Required is not enforced for such beans.
As an aside, You might think that where did this SKIP_REQUIRED_CHECK_ATTRIBUTE attribute come from and where is it set: it is set on the instances of BeanDefinition that are used by Spring internally for bean creation and post-processing.
If you really want to enforce the #Required constraints, you would have to override the RequiredAnnotationBeanPostProcessor, override the boolean shouldSkip(..) method and register this class instead of the default RequiredAnnotationBeanPostProcessor. And as the documentation for RequiredAnnotationBeanPostProcessor says:
A default RequiredAnnotationBeanPostProcessor will be registered by the "context:annotation-config" and "context:component-scan" XML tags. Remove or turn off the default annotation configuration there if you intend to specify a custom RequiredAnnotationBeanPostProcessor bean definition.
Another way would be to use the initMethod attribute on your #Bean annotation. Which could perform checks to see that the required properties are indeed set. However, since this is code based configuration, you could just as well call that init method yourself.
Also, in my opinion, there is not much point in going through a lot of trouble to use your own RequiredAnnotationBeanPostProcessor, as the following documentation says:
Please note that an 'init' method may still need to implemented (and may still be desirable), because all that this class does is enforce that a 'required' property has actually been configured with a value. It does not check anything else... In particular, it does not check that a configured value is not null.
So, to summarize: #Required doesn't work with #Configuration classes by default. If you need to make sure that all your properties are set, you can just as well do it yourself when you create the bean in the #Bean methods (By calling some init method that performs such validations, or just supplying the required properties yourself). And if you really need to make the #Required annotation work, you'd need to use your own implementation of the RequiredAnnotationBeanPostProcessor, register it as a bean in the spring context and give up the benefits of context:annotation-config.
Just tried to declare a #Bean RequiredAnnotationBeanPostProcessor with overridden shouldSkip() method.
Yes, it checks my beans, but it fails even if I set all the required properties, i.e. it always fails.
I think Spring has a real problem with supporting #Required annotation for Java Config, since Spring has no way to tell whether or not you have set the property when you do it directly in Java code. (It can't inspect for 'null' fields later, since this would mean changing the semantics of the #Required annotation which should allow explicitly set null values).
When you use an XML config, Spring creates a wrapper object to set the properties, so it can track all the configured 'setXxx()' operations.
Conclusion: there is no reasonable way to enable #Required annotation for beans created in Java #Configuration classes.
(Very unfortunate feature, in my opinion, since the bean class writer and the class user might be different persons).

How can I programmatically create a transactional proxy?

I created a bean instance with
Type instance = new Type();
and autowired it using
ctx.getAutowireCapableBeanFactory().autowireBean(instance);
The beans run method is executed by a ThreadPoolTaskExecuter and is annotated with the
#Transactional annotation which has no effect in this case. Since lazy loading problems occur. I need a transaction here.
How can I create a transactional proxy and wrap my instance?
Is there a way other than using transaction-manager manually?
You should get the correct proxy if you apply BeanPostProcessors from the context:
instance = ctx.getAutowireCapableBeanFactory().applyBeanPostProcessorsAfterInitialization(instance);
You can certainly create a PlatformTransactionManager subclass manually and use its methods to create and commit or rollback transactions.
If you want to proxy an object, the class you probably want is org.springframework.transaction.interceptor.TransactionProxyFactoryBean. Setup an instance of that and call getObject() to get your proxied class.

Categories