How do i inject a spring bean into a hibernate SequenceGenerator - spring

I have a custom SequenceGenerator written for hibernate:
public class LoginGenerator extends SequenceGenerator {
#Autowired
ITicketService ticketService;
#Override
public Serializable generate(SessionImplementor session, Object obj) {
Ticket ticket = (Ticket) obj;
Long maxCounterOfSection = ticketService.findMaxSectionCounter(ticket
.getSection());
maxCounterOfSection++;
return ticket.getSection() + "-" + maxCounterOfSection;
}
}
But i dont have a spring context inside this generator! ticketService is null. i already tried #Component annotation for my generator, but without success.
PS: Im using spring 3.2.0-FINAL and hibernate 3.6.10-FINAL and there is no way updating to hibernate4!
Any idea, anyone?

Problem solved with ApplicationContextAware class, as described above.
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
#SuppressWarnings("static-access")
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
public static <T> T getBean(String name, Class<T> requiredType) {
return applicationContext.getBean(name, requiredType);
}
}
In applicationContext.xml I added <bean id="applicationContextProvider" class="de.gfz.rz.spring.ApplicationContextProvider"></bean>.
And here the usage:
public class LoginGenerator extends SequenceGenerator {
#Override
public Serializable generate(SessionImplementor session, Object obj) {
ITicketService ticketService = ApplicationContextProvider
.getBean(ITicketService.class);
Ticket ticket = (Ticket) obj;
Long maxCounterOfSection = ticketService.findMaxSectionCounter(ticket
.getSection());
maxCounterOfSection++;
return ticket.getSection() + "-" + maxCounterOfSection;
}
}

Related

Spring #Autowired field returns null on type of PropertySourcesPlaceholderConfigurer bean object

my colde below
/* this bean is the bean that alwyas returned as null*/
#Component
public class ContextHolder implements ApplicationContextAware, ServletContextAware {
private static ApplicationContext springContext;
private static ServletContext servletContext;
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.springContext = applicationContext;
}
public static ApplicationContext getSpringContext() {
return springContext;
}
public static ServletContext getServletContext() {
return servletContext;
}
}
#Component
#PropertySource("classpath:appProps/globals-${spring.profiles.active}.properties")
public class PropertyManager extends PropertySourcesPlaceholderConfigurer {
#Autowired
ContextHolder contextHolder; // this binding alwasy return null. I don't know the why
public String get(String key) {
// NullpointerException occurs, because contextHolder is null
// But ContextHodler.getSpringContext() static access works, but I want to know
// why Autowired does not work
return contextHolder.getSpringContext().getEnvironment().getProperty(key);
}
}
I want to know why Autowired does not work.
I have search the answer in google. but there no documents about this.
I guess PropertySourcesPlaceholderConfigurer is type of BeanFactoryPostProcessor, So when initialize the PropertySourcesPlaceHolderConfiguer there are no bean of ContextHolder instance in memory, thus contextHolder is null??

Point cut is not triggered when AspectJExpressionPointcutAdvisor create programatically

I am creating AspectJExpressionPointcutAdvisor based on number of pointcut present in application properties file .It's creating object without error but pointcut are not triggered.
Note: Need to create bean dynamically based on number of pointcut expression in properties file (varies).
Application properties file
pointcut.expression.projectUpdate[0]= execution(* com.abc.app.service.impl.TestServiceImpl.updateProjectDetails(..))
pointcut.expression.projectUpdate[1]= execution(* com.abc.app.service.impl.TestServiceImpl.cancelProject(..))
pointcut.expression.projectUpdate[2]= execution(* com.abc.app.service.impl.TestCSATRatingServiceImpl.saveRatingDetails(..))
TestConfig.class
#Configuration
public class TestConfig implements BeanFactoryAware {
#Autowired
private PointcutExprProperties pcExprProp;
#Autowired(required=false)
private ProjectUpdateAspect projectUpdateAdvice;
private BeanFactory beanFactory;
#Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
#PostConstruct
public void configure() {
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
int i=1;
for(String pointCut : pcExprProp.getProjectUpdate()) {
AspectJExpressionPointcutAdvisor projectUpdateAdvisor = new AspectJExpressionPointcutAdvisor();
projectUpdateAdvisor.setExpression(pointCut);
projectUpdateAdvisor.setAdvice(projectUpdateAdvice);
configurableBeanFactory.registerSingleton("beanName_"+i, projectUpdateAdvisor);
i++;
}
}
}
ProjectUpdateAspect.class
#Component
#Aspect
public class ProjectUpdateAspect implements AfterReturningAdvice {
private static final Logger log = LoggerFactory.getLogger(ProjectUpdateAspect.class);
#Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
try {
// some thing
}catch (Exception exception) {
log.error("Error while processing ProjectUpdateAspect",exception);
}
}
}
PointcutExprProperties
#Configuration
#ConfigurationProperties(prefix = "pointcut.expression")
#Validated
public class PointcutExprProperties {
#NotNull
private List<String> projectCreate;
#NotNull
private List<String> projectUpdate;
public List<String> getProjectCreate() {
return projectCreate;
}
public void setProjectCreate(List<String> projectCreate) {
this.projectCreate = projectCreate;
}
public List<String> getProjectUpdate() {
return projectUpdate;
}
public void setProjectUpdate(List<String> projectUpdate) {
this.projectUpdate = projectUpdate;
}
}
Please suggest me how to get rid of this issue.
I suggest you do it like this:
You do not define your "aspect" as #Component #Aspect but make it implement MethodInterceptor.
You create AspectJExpressionPointcut with the value from your properties file.
You register a DefaultPointcutAdvisor (configured with your pointcut and interceptor) as a bean.
See also my answer here (update 3) and my GitHub sample repository which I just updated for you in order to include reading the pointcut from application.properties.

Injecting Spring Dependencies into ConstrantValidator

I'm using Bean Validation. I have a custom validator #MyValidator that needs to look up a value with an injected Spring managed DAO object. How can I get access to this? Spring isn't injecting the DAO into my "MyValidator" object.
#Component
public class CodeListValidator implements ConstraintValidator<CodeList, String> {
#Autowired
private ICodeListCommonService codeListCommonService;
private CodeListEnum codeListID;
#Override
public void initialize(CodeList constraintAnnotation) {
this.codeListID = constraintAnnotation.value();
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return codeListCommonService.doesCodeEntityExistForCodeList(codeListID.getDbCodeListId(), value, ProviderConstants.CODE_LIST_STATUS_ACTIVE);
}
}
The "codeListCommonService" is null. This is because Spring isn't creating the class - but how can I get this to work with Spring AoP?
The use of this validator looks like this:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
MyObject validateMe = new MyObject();
Set<ConstraintViolation<MyObject>> constraintViolations = validator.validate(validateMe);
For MyObject:
public class MyObject {
#Size(max = 1)
#CodeList(CodeListEnum.CARTYPE)
public String carType;
}
So when the validator runs, it processes the annotations... I just need to get a service injected into the CodeListValidator I made to it can do a DB lookup to verify the value against the DB list of "valid car type values".
EDIT: The solution:
Played around with the idea of making a Spring aware factory- too much integration with existing code.
The solution that seems the best (and it works here) is to make a Spring service that stores the ApplicationContext in a static method so "non-Spring managed" beans can get to them.
So a new service:
#Service
public class SpringApplicationContextService implements ISpringApplicationContextService, ApplicationContextAware {
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
And then any validator or non-Spring bean can get at the Spring beans via:
public class CodeListValidator implements ConstraintValidator<CodeList, String> {
#Autowired
private ICodeListCommonService codeListCommonService;
private CodeListEnum codeListID;
#Override
public void initialize(CodeList constraintAnnotation) {
this.codeListID = constraintAnnotation.value();
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
ICodeListCommonService codeListCommonService = SpringApplicationContextService.getApplicationContext().getBean(ICodeListCommonService.class);
return codeListCommonService.doesCodeEntityExistForCodeList(codeListID.getDbCodeListId(), value, ProviderConstants.CODE_LIST_STATUS_ACTIVE);
}
}
And, of course, the reason for all of this (which is used dozens of times in this app):
#CodeList(CodeListEnum.EMAIL_OPT_OUT_FLAG)
public String emailOptOutFlag;
#CodeList(CodeListEnum.CLEARING_HOUSE)
public String clearingHouse;
The minimum setup for #Autowired to work properly in ConstraintValidator implementation is to have this bean in a Spring #Configuration:
#Bean
public Validator defaultValidator() {
return new LocalValidatorFactoryBean();
}
This allows any beans, including ApplicationContext, to be injected directly into a ConstraintValidator:
#Constraint(validatedBy = DemoValidator.class)
public #interface DemoAnnotation {
// ...
Class<?> beanClass();
}
public class DemoValidator implements ConstraintValidator<DemoAnnotation, String> {
private final ApplicationContext applicationContext;
private Object bean;
#Autowired
public DemoValidator(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public void initialize(DemoAnnotation constraint) {
Class<?> beanClass = constraint.beanClass();
bean = applicationContext.getBean(beanClass);
}
#Override
public boolean isValid(String obj, ConstraintValidatorContext context) {
return !obj.isEmpty();
}
}
Demo
For a really flexible validation solution I would recommend Jakub Jirutka's Bean Validator utilizing Spring Expression Language (SpEL) which allows things like:
public class Sample {
#SpELAssert("#myService.calculate(#this) > 42")
private int value;
}

Couldn't find PersistentEntity for type class when using #EnableMongoAuditing

I am getting "Couldn't find PersistentEntity for type class" error when I am using #EnableMongoAuditing features along with MongoRepository.
This happens when I save a document when collection isn't already present in database.
I tried whatever is mentioned in:
https://github.com/spring-projects/spring-boot/issues/12023
https://jira.spring.io/browse/DATAMONGO-1999
Spring boot mongodb auditing error
but nothing is working.
Mentioned things are:
Extend MongoConfig by AbstractMongoConfiguration and override all methods.
Here is my code which reproduced the same error:
MongoConfig class
#Configuration
public class MongoConfig extends AbstractMongoConfiguration {
#Value("${spring.data.mongodb.host}")
private String mongoHost;
#Value("${spring.data.mongodb.port}")
private String mongoPort;
#Value("${spring.data.mongodb.database}")
private String mongoDB;
#Override
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(new MongoClient(mongoHost + ":" + mongoPort), mongoDB);
}
#Override
public MongoClient mongoClient() {
return new MongoClient(mongoHost, Integer.parseInt(mongoPort));
}
#Override
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoDbFactory());
}
#Override
public MappingMongoConverter mappingMongoConverter() {
return new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()), new MongoMappingContext());
}
#Override
protected String getDatabaseName() {
return mongoDB;
}
}
Person Collection class
#Document
public class Person {
#Id
private String id;
private String name;
#CreatedDate
private LocalDateTime createdAt;
#LastModifiedDate
private LocalDateTime lastModified;
// Getter Setters Constructors omitted for brevity
}
Main Application class
#EnableMongoAuditing
#EnableMongoRepositories ({"com.example.*", "org.apache.*"})
#SpringBootApplication
#ComponentScan({"com.example.*", "org.apache.*"})
public class DemoApplication implements CommandLineRunner {
#Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
Person p1 = new Person("1", "prakhar");
personRepository.save(p1);
}
}
Expected Result is Person entity should be saved in database.
Actual Result is "Couldn't find PersistentEntity for type class Person" error
Looks like you ran into https://github.com/spring-projects/spring-boot/issues/12023
Extending AbstractMongoConfiguration will switch off Spring Boot's auto-configuration of various Mongo components and also customises the base packages that are used to scan for mappings. I would recommend that you don't use it in Spring Boot.
Update
I managed to get the example running with the configuration as simple as
#Configuration
public class MongoConfig {
#Value("${spring.data.mongodb.host}")
private String mongoHost;
#Value("${spring.data.mongodb.port}")
private String mongoPort;
#Value("${spring.data.mongodb.database}")
private String mongoDB;
#Bean
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(new MongoClient(mongoHost + ":" + mongoPort), mongoDB);
}
#Bean
public MongoClient mongoClient() {
return new MongoClient(mongoHost, Integer.parseInt(mongoPort));
}
}
and the app class
#EnableMongoAuditing
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
#Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
Thread.sleep(2000);
Person p1 = new Person("1", "prakhar");
personRepository.save(p1);
}
}
Notice that I followed my own advice and did't inherit from AbstractMongoConfiguration
Explanation
The problem lies in the initialization of
#Bean
public MappingMongoConverter mappingMongoConverter() {
return new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()), new MongoMappingContext());
}
You simply call MongoMappingContext constructor, without calling setInitialEntitySet. Compare that with MongoDataConfiguration auto-configuration class.
#Bean
#ConditionalOnMissingBean
public MongoMappingContext mongoMappingContext(MongoCustomConversions conversions)
throws ClassNotFoundException {
MongoMappingContext context = new MongoMappingContext();
context.setInitialEntitySet(new EntityScanner(this.applicationContext)
.scan(Document.class, Persistent.class));
Class<?> strategyClass = this.properties.getFieldNamingStrategy();
if (strategyClass != null) {
context.setFieldNamingStrategy(
(FieldNamingStrategy) BeanUtils.instantiateClass(strategyClass));
}
context.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
return context;
}
Even worse, you don't register MongoMappingContext as a managed bean.
Due to this fact, auto-configuration class is still created. This leads to a race condition, I tried to run the original code and could easily reproduce the error, but with a breakpoint in AbstractMappingContext.addPersistentEntity the test always passed.
For me I resolved this issue by adding following method in MongoConfig if your class extends from AbstractMongoConfiguration
#Override
protected String getMappingBasePackage() {
return "com.companyName.modulename"
}
If MongoConfig extends from MongoConfigurationSupport then add below method
#Override
protected Collection<String> getMappingBasePackages() {
return Arrays.asList("com.companyName.module1","com.companyName.module2");
}
Note that in later case I can specify multiple package names as base packages.

Spring Boot - Auto wiring service having String constructor

How do i #autowire bean class TransactionManagerImpl which is having 1(String) argument constructor without using new in spring-boot application?
Even after searching through many post i couldn't get any clue to autowire without using new
I need to autowire TransactionManager in three different classes and the parameters are different in all three classes.
This looks like very basic scenario.
#Service
public class TransactionManagerImpl implements TransactionManager {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
String txnLogFile;
#ConstructorProperties({"txnLogFile"})
public TransactionManagerImpl(String txnLogFile) {
this.txnLogFile= txnLogFile;
}
}
is there any specific requirement where you want to use #Service annotation?
if not then you can use #Bean to create a bean for TransactionManagerImpl like below.
#Configuration
public class Config {
#Value("${txnLogFile}")
private String txnLogFile;
#Bean
public TransactionManager transactionManager() {
return new TransactionManagerImpl(txnLogFile);
}
}
and remove #Service annotation from TransactionManagerImpl.
Putting aside other complications, it can be done like this
public TransactionManagerImpl(#Value("${txnLogFile}") String txnLogFile) {
this.txnLogFile= txnLogFile;
}
Finally, i did it as below, now sure if this is the best way to do. I did not want to have three implementation just because of one variable.
application.yaml
app:
type-a:
txn-log-file: data/type-a-txn-info.csv
type-b:
txn-log-file: data/type-b-txn-info.csv
default:
txn-log-file: data/default/txn-info.csv
MainApplication.java
#SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(MainApplication.class).web(WebApplicationType.NONE).run(args);
}
#Bean
public TransactionManager transactionManager(#Value("${app.default.txn-log-file}") String txnLogFile) {
return new TransactionManagerImpl(txnLogFile);
}
#Bean
public CsvService csvService(String txnLogFile) {
return new CsvServiceImpl(txnLogFile);
}
}
TypeOneRoute.java
#Configuration
public class TypeOneRoute extends RouteBuilder {
#Value("${app.type-a.txn-log-file}")
private String txnLogFile;
#Autowired
private ApplicationContext applicationContext;
private TransactionManager transactionManager;
#Override
public void configure() throws Exception {
transactionManager = applicationContext.getBean(TransactionManager.class, txnLogFile);
transactionManager.someOperation();
}
}
TypeTwoRoute.java
#Configuration
public class TypeTwoRoute extends RouteBuilder {
#Value("${app.type-b.txn-log-file}")
private String txnLogFile;
#Autowired
private ApplicationContext applicationContext;
private TransactionManager transactionManager;
#Override
public void configure() throws Exception {
transactionManager = applicationContext.getBean(TransactionManager.class, txnLogFile);
transactionManager.create();
}
}
TransactionManager.java
#Service
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public interface TransactionManager {
public ZonedDateTime create() throws IOException, ParseException;
}
TransactionManagerImpl.java
public class TransactionManagerImpl implements TransactionManager {
#Autowired
private ApplicationContext applicationContext;
private String txnLogFile;
public TransactionManagerImpl(String txnLogFile) {
this.txnLogFile = txnLogFile;
}
private CsvService csvService;
#PostConstruct
public void init() {
csvService = applicationContext.getBean(CsvService.class, txnLogFile);
}
public ZonedDateTime create() throws IOException, ParseException {
try {
csvService.createTxnInfoFile();
return csvService.getLastSuccessfulTxnTimestamp();
} catch (IOException e) {
throw new IOException("Exception occured in getTxnStartDate()", e);
}
}
}
Initially TransactionManager Bean will be registered with the app.default.txn-info.csv and when i actually get it from ApplicationContext i am replacing the value with the parameter passed to get the bean from ApplicationContext

Resources