mongock #BeforeExecution is not executed - spring

I need to initialize admin from env variables, but #BeforeExecution method is not launched, execution directly starts in initAdmin method. Any suggestions?
#ChangeUnit(id = "init", order = "001", author = "me")
#RequiredArgsConstructor
#Component
public class InitChangeLog {
private final MyService service;
private final MongoTemplate template;
private final ConfigurableEnvironment env;
private String admin;
#BeforeExecution
private void setAdmin() {
this.admin = env.getProperty("admin");
}
#Execution
public void initAdmin() {
service.create(...);
}
}

I can see multiple errors there that can be the reason of your issue:
#Component shouldn't be used for a changeUnit. The way Mongock retrieves the changeUnits is by specifying the packages or directly the class. You can see it in the documentation
Mongock offers two ways to inject your beans, in the constructor and directly in the method. Please see this section in the documentation for more information
#BeforeExecution shouldn't be private.
Try correcting those points and I am confident it will work just fine :)

Related

Is it possible to read property file values inside #Repository?

Is it possible to read property file values inside #Repository.
Any help is appreciated.
As M.Deinum mentioned in the comment section, #Repository is just a variation of #Component. You can read your property file by injecting Environment or you can just use #Value.
If you use a class repository, you can use the #Value annotation on any non-final field to have it populated automatically.
On the other hand, if you are using an interface repository, you cannot read a property directly, as you have nowhere to inject your components/values.
Anyway, I found a sort of work-around which can be used to achieve a similar result.
You start by creating a class that implements ApplicationContextProvider:
#Component
public class StaticPropertiesProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(#Nonnull final ApplicationContext applicationContext) throws BeansException {
StaticPropertiesProvider.applicationContext = applicationContext;
}
public static <T> T getProperty(final String property, final Class<T> clazz) {
return applicationContext.getEnvironment().getProperty(property, clazz);
}
}
When your application starts, the static applicationContext field will be populated with your Application Context, so that you can use it afterwards from the static getProperties method.
You can then call it from a default method inside your repository, e.g.
public interface MyRepository extends MongoRepository<MyDocument, String> {
#Query(value = "{'myProperty': ?0}")
List<MyDocument> findByMyProperty(int myProperty);
default List<MyDocument> findByMyProperty() {
final var myPropertyDefault = StaticPropertiesProvider.getProperty("my.property", Integer.class);
return this.findByMyProperty(myProperty);
}
}
Clearly there should be a property name my.property with an int value inside application.properties.
(you can do the same even with JPA repositories)

Is it possible to have a constant valued through a Spring Service?

We have a web service that one of its parameters is called origin and this origin is always validated against a code in the database.
For each one of our services I have to validate this code. This code does not change so I want to keep it in a constant, but I still have to validate it to prevent clients from sending a wrong code.
Basically what I want is this:
#Service
public class Service {
#Autowired
private LogBS logBS;
// I know this cannot be used in a static context.
public static final Long CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
public void validateOriginCode(final Long origin) {
if (!origin.equals(CODE)) {
throw new ServiceException("Wrong origin code!");
}
}
}
I know something similar can be done with Spring caching, but is it possible to do it with a constant?
I would rather go with this:
#Service
public class CodeValidatorService {
private LogBS logBS;
private Long CODE;
#Autowired
public CodeValidatorService(LogBS logBS){
this.logBS = logBS;
CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
if (CODE == null){
throw new ServiceException("Code cannot be read from DB!");
}
}
public void validateOriginCode(final Long origin) {
if (!origin.equals(CODE)) {
throw new ServiceException("Wrong origin code!");
}
}
}
Just as a code review, I prefer injecting dependencies in the constructor rather than using #Autowired in the field directly, it makes the service testable. You could also try to read the code in a #PostConstruct method, but I think it's better to do it in the constructor so you always have the service in a ready-to-go state.
For using it in the rest of your services, inject the CodeValidatorService instance on them:
#Service
public class OtherService {
private CodeValidatorService codeValidatorService;
#Autowired
public OtherService(CodeValidatorService codeValidatorService){
this.codeValidatorService = codeValidatorService;
}
public void performAction(final Long origin) {
codeValidatorService.validateOriginCode(origin);
//do the rest of your logic here
}
}
See also:
Spring Beans and dependency injection
Setter injection versus constructor injection
You can have a constantsProvider class
#Component
public class ConstantsProvider {
#Autowired
private LogBS logBS;
private String CODE;
#PostConstruct
public void init() {
CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
}
public String getCode() {
return CODE;
}
}
Add this snippet of code to Service class
#Autowired
private ConstantsProvider constantsProvider;
You can use constantsProvider.getCode() in your services. This way CODE is going to be immutable and not defined in a static context.
Note: If you have more constants similar to this, there is a better way to define the ConstantsProvider class. If there is only one, I would stick to the above implementation.
Edit 1:
If you need it in all the service classes, make the constantsProvider a spring bean and initialize the CODE there itself. Updated the answer

Spring #ConfigurationProperties not populated

I am experiencing problems using the #ConfigurationProperties feature.
Probably, I am missing something, since the mechanism seems very simple, but for me, it does not work.
I am using Spring Boot with the following main Application class
#SpringBootApplication
#EnableAspectJAutoProxy
#EnableConfigurationProperties(QueuesProperties.class)
#PropertySource("file:config/queues.properties")
#ImportResource("classpath:/spring-config.xml")
public class Application {
public static void main(String... args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
with QueuesProperties
#ConfigurationProperties(prefix = "wmq.in.queue")
public class QueuesProperties {
private static final Logger LOGGER = LoggerFactory.getLogger(QueuesProperties.class);
private String descr;
public String getDescr() {
return descr;
}
public void setDescr(String descr) {
this.descr = descr;
}
}
The properties file is very simple (I am trying to isolate the problem)
wmq.in.queue.descr = description
Then, I am trying to #Autowired the QueuesProperties in a #Component that I use in a spring-integration flow with a .
The QueuesProperties is correctly injected but the descr attribute is null.
#Autowired
private QueuesProperties queuesConfiguration;
while this
#Value("${wmq.in.queue.descr}")
private String descr;
is correctly evaluated.
I have made a lot of attempt with different configurations or code, but the result is the same. I get the QueuesProperties bean but it is not populated.
What am I missing?
Reading the question isn't very clear if the wmq.in.queue.descr = description properties is written in applciation.properties file. I said it because you say that the properties is correctly evaluated with #Value and not with
#Autowired
private QueuesProperties queuesConfiguration;
Even the #PropertySource("file:config/queues.properties") let me to think that probably the your wmq.in.queue.descr = description properties isn't written in applciation.properties but in file:config/queues.properties.
Summing
For use #ConfigurationProperties feature you have write the properties in application.properties and use #EnableConfigurationProperties(QueuesProperties.class) on #Component, #Configuration and so on annotated classes like below.
#Component
#EnableConfigurationProperties(QueuesProperties.class)
public class YourBean {
....
private final QueuesProperties queuesProperties;
public YourBean(QueuesProperties queuesProperties){
this.queuesProperties = queuesProperties;
}
.....
}
actually you can change the application.properties file name customizing spring boot properties evaluation but for your local app I discourage. I consider application.properties a good name for naming a place in which you put the configuration properties of your application
I hope that it can help you

Spring job launcher not able to access services

Hi Still learning hope someone can fill in the blanks
I have JSVC quartz spring batch system that has been running for over a year. The Job launcher needs to connect to 2 spring services that successfully run in other parts of the system. Each service has a number of sql repositories or services injected into it.
Please make note of package declarations. for the application context entry.
package com.mycompany.business.services.impl;
....
#Service
public class BatchProcessService {
private final DomainSepcificRepository1 rep1;
...
private final DomainSepcificRepositoryN repN;
#Inject
public BatchProcessService(Final final DomainSepcificRepository1 rep1,..., final DomainSepcificRepositoryN repN) {
// injected values assigned to instance variables.
}
public List <...> findByCriteria(.....)(
.....
}
}
and
package com.mycompany.business.services.impl;
....
#Service
public class EmailNotificationServiceImpl implements EmailNotificationService {
private UserService userService;
private final MailMessage mailMessage;
private final MailTransport mailTransport;
#Inject
public EmailNotificationServiceImpl(final UserService userService, final MailMessage mailMessage, final MailTransport mailTransport) {
this.userService = userService;
this.mailMessage = mailMessage;
this.mailTransport = mailTransport;
}
.....
public void notifySupportStaff(....){
.....
}
}
In my application context xml file, there is the following line that should allow my job launcher to see and instantiate the above services. I think "base-package=" specifies the packages to look for #services, #components and #repositories that can be injected.
<context:component-scan base-package="com.mycompany.common.batch, com.mycompany.batch, com.mycompany.business.services" >
<context:exclude-filter type="assignable" expression="com.mycompany.common.batch.scheduler.service.MyCompanySchedulerService"/>
</context:component-scan>
I think that #Component should allow this class to see the services and instaniate them and any dependancies (other services) they have.
For some reason the jsvc system only wants to invoke class below with the NO arg constructor. and it is not injecting the 2 services.
My unit tests are able to test the method using the service only if I provide a 2 argument constructor for MOCK services.
lll
Any thoughts on why batch system cannot inject the dependencies?
package com.mycompany.batch.scheduler;
....
#Inject
private BatchProcessService batchProcessService;
#Inject
private EmailNotificationService emailNotificationService;
#Component
public class MyCompanySchedulerJobLauncher extends SchedulerJobLauncher {
public MyCompanySchedulerJobLauncher() {
super();
}
// #Inject
public MyCompanySchedulerJobLauncher(final BatchProcessService batchProcessService, final EmailNotificationService emailNotificationService) {
super();
this.batchProcessService = batchProcessService;
this.emailNotificationService = emailNotificationService;
}
#Override
public int processJob(final JobExecutionContext context) throws JobRestartException, JobExecutionAlreadyRunningException, ParseException {
......
if(batchProcessSerive.findByCriteria(....).size() == 0) {
emailNotificationService.notifySupport(...)
}
}
Well Don't I feel silly.
The problem was that at the point where I was assuming I could/would inject dependancies. The application context was private. Once I made my application context protected and to get the services. all worked

#Autowired in static classes

This is an Spring MVC project with Hibernate.
I'm, trying to make a Logger class that, is responsible for inputting logs into database.
Other classes just call proper methods with some attributes and this class should do all magic.
By nature it should be a class with static methods, but that causes problems with autowiring dao object.
public class StatisticLogger {
#Autowired
static Dao dao;
public static void AddLoginEvent(LogStatisticBean user){
//TODO code it god damn it
}
public static void AddDocumentEvent(LogStatisticBean user, Document document, DocumentActionFlags actionPerformed){
//TODO code it god damn it
}
public static void addErrorLog(Exception e, String page, HashMap<String, Object> parameters){
ExceptionLogBean elb=new ExceptionLogBean();
elb.setStuntDescription(e);
elb.setSourcePage(page);
elb.setParameters(parameters);
if(dao!=null){ //BUT DAO IS NULL
dao.saveOrUpdateEntity(elb);
}
}
How to make it right? What should I do not to make dao object null?
I know that I could pass it as a method parameter, but that isn't very good.
I'm guessing that autowired can't work on static objects, because they are created to early to autowiring mechanism isn't created yet.
You can't #Autowired a static field. But there is a tricky skill to deal with this:
#Component
public class StatisticLogger {
private static Dao dao;
#Autowired
private Dao dao0;
#PostConstruct
private void initStaticDao () {
dao = this.dao0;
}
}
In one word, #Autowired a instance field, and assign the value to the static filed when your object is constructed. BTW, the StatisticLogger object must be managed by Spring as well.
Classical autowiring probably won't work, because a static class is not a Bean and hence can't be managed by Spring. There are ways around this, for example by using the factory-method aproach in XML, or by loading the beans from a Spring context in a static initializer block, but what I'd suggest is to change your design:
Don't use static methods, use services that you inject where you need them. If you use Spring, you might as well use it correctly. Dependency Injection is an Object Oriented technique, and it only makes sense if you actually embrace OOP.
I know this is an old question but just wanted to share what I did,
the solution by #Weibo Li is ok but the problem it raises Sonar Critical alert about assigning non static variable to a static variable
the way i resolved it with no sonar alerts is the following
I change the StatisticLogger to singlton class (no longer static)
like this
public class StatisticLogger {
private static StatisticLogger instance = null;
private Dao dao;
public static StatisticLogger getInstance() {
if (instance == null) {
instance = new StatisticLogger();
}
return instance;
}
protected StatisticLogger() {
}
public void setDao(Dao dao) {
this.dao = dao;
}
public void AddLoginEvent(LogStatisticBean user){
//TODO code it god damn it
}
public void AddDocumentEvent(LogStatisticBean user, Document document, DocumentActionFlags actionPerformed){
//TODO code it god damn it
}
public void addErrorLog(Exception e, String page, HashMap<String, Object> parameters){
ExceptionLogBean elb=new ExceptionLogBean();
elb.setStuntDescription(e);
elb.setSourcePage(page);
elb.setParameters(parameters);
if(dao!=null){
dao.saveOrUpdateEntity(elb);
}
}
I created a service(or Component) that autowire the service that i want and set it in the singlton class
This is safe since in spring it will initialize all the managed beans before doing anything else and that mean the PostConstruct method below is always called before anything can access the StatisticLogger
something like this
#Component
public class DaoSetterService {
#Autowired
private Dao dao0;
#PostConstruct
private void setDaoValue () {
StatisticLogger.getInstance().setDao(dao0);
}
}
Instead of using StatisticLogger as static class I just use it as StatisticLogger.getInstance() and i can access all the methods inside it
You can pass the DAO to StatisticLogger from where you call it.
public static void AddLoginEvent(LogStatisticBean user, DAO dao){
dao.callMethod();
}
It might be too late to put an answer to this question, especially when a question is already having an accepted answer. But it might help others in case they face the same issue.
inside the StatisticLogger class create an instance of the Dao service.
public static Dao daoService = new Dao();
then, auto-wire the service instance through the constructor of the StatisticLogger class.
#Autowired
public functionName(Dao daoService0) {
this.daoService = daoService0;
}
//use this service as usual in static class
daoService.fun();
I think this is the simplest solution for the problem.

Resources