Injecting externalized value into Spring annotation - java

I've been thinking around the Java feature that evaluates annotation values in compile-time and it seems to really make difficult externalizing annotation values.
However, I am unsure whether it is actually impossible, so I'd appreciate any suggestions or definitive answers on this.
More to the point, I am trying to externalize an annotation value which controls delays between scheduled method calls in Spring, e.g.:
public class SomeClass {
private Properties props;
private static final long delay = 0;
#PostConstruct
public void initializeBean() {
Resource resource = new ClassPathResource("scheduling.properties");
props = PropertiesLoaderUtils.loadProperties(resource);
delay = props.getProperties("delayValue");
}
#Scheduled(fixedDelay = delay)
public void someMethod(){
// perform something
}
}
Suppose that scheduling.properties is on classpath and contains property key delayValue along with its corresponding long value.
Now, this code has obvious compilation errors since we're trying to assign a value to final variable, but that is mandatory, since we can't assign the variable to annotation value, unless it is static final.
Is there any way of getting around this? I've been thinking about Spring's custom annotations, but the root issue remains - how to assign the externalized value to annotation?
Any idea is welcome.
EDIT: A small update - Quartz integration is overkill for this example. We just need a periodic execution with sub-minute resolution and that's all.

The #Scheduled annotation in Spring v3.2.2 has added String parameters to the original 3 long parameters to handle this. fixedDelayString, fixedRateString and initialDelayString are now available too:
#Scheduled(fixedDelayString = "${my.delay.property}")
public void someMethod(){
// perform something
}

Thank you both for your answers, you have provided valuable info which led me to this solution, so I upvoted both answers.
I've opted to make a custom bean post processor and custom #Scheduled annotation.
The code is simple (essentially it is a trivial adaptation of existing Spring code) and I really wonder why they didn't do it like this from the get go. BeanPostProcessor's code count is effectively doubled since I chose to handle the old annotation and the new one.
If you have any suggestion on how to improve this code, I'll be glad to hear it out.
CustomScheduled class (annotation)
#Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface CustomScheduled {
String cron() default "";
String fixedDelay() default "";
String fixedRate() default "";
}
CustomScheduledAnnotationBeanPostProcessor class
public class CustomScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, EmbeddedValueResolverAware, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, DisposableBean
{
private static final Logger LOG = LoggerFactory.getLogger(CustomScheduledAnnotationBeanPostProcessor.class);
// omitted code is the same as in ScheduledAnnotationBeanPostProcessor......
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// processes both #Scheduled and #CustomScheduled annotations
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
final Class<?> targetClass = AopUtils.getTargetClass(bean);
ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Scheduled oldScheduledAnnotation = AnnotationUtils.getAnnotation(method, Scheduled.class);
if (oldScheduledAnnotation != null) {
LOG.info("#Scheduled found at method {}", method.getName());
Assert.isTrue(void.class.equals(method.getReturnType()), "Only void-returning methods may be annotated with #Scheduled.");
Assert.isTrue(method.getParameterTypes().length == 0, "Only no-arg methods may be annotated with #Scheduled.");
if (AopUtils.isJdkDynamicProxy(bean)) {
try {
// found a #Scheduled method on the target class for this JDK proxy -> is it
// also present on the proxy itself?
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
} catch (SecurityException ex) {
ReflectionUtils.handleReflectionException(ex);
} catch (NoSuchMethodException ex) {
throw new IllegalStateException(String.format(
"#Scheduled method '%s' found on bean target class '%s', " +
"but not found in any interface(s) for bean JDK proxy. Either " +
"pull the method up to an interface or switch to subclass (CGLIB) " +
"proxies by setting proxy-target-class/proxyTargetClass " +
"attribute to 'true'", method.getName(), targetClass.getSimpleName()));
}
}
Runnable runnable = new ScheduledMethodRunnable(bean, method);
boolean processedSchedule = false;
String errorMessage = "Exactly one of 'cron', 'fixedDelay', or 'fixedRate' is required.";
String cron = oldScheduledAnnotation.cron();
if (!"".equals(cron)) {
processedSchedule = true;
if (embeddedValueResolver != null) {
cron = embeddedValueResolver.resolveStringValue(cron);
}
cronTasks.put(runnable, cron);
}
long fixedDelay = oldScheduledAnnotation.fixedDelay();
if (fixedDelay >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
fixedDelayTasks.put(runnable, fixedDelay);
}
long fixedRate = oldScheduledAnnotation.fixedRate();
if (fixedRate >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
fixedRateTasks.put(runnable, fixedRate);
}
Assert.isTrue(processedSchedule, errorMessage);
}
CustomScheduled newScheduledAnnotation = AnnotationUtils.getAnnotation(method, CustomScheduled.class);
if (newScheduledAnnotation != null) {
LOG.info("#CustomScheduled found at method {}", method.getName());
Assert.isTrue(void.class.equals(method.getReturnType()), "Only void-returning methods may be annotated with #CustomScheduled.");
Assert.isTrue(method.getParameterTypes().length == 0, "Only no-arg methods may be annotated with #CustomScheduled.");
if (AopUtils.isJdkDynamicProxy(bean)) {
try {
// found a #CustomScheduled method on the target class for this JDK proxy -> is it
// also present on the proxy itself?
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
} catch (SecurityException ex) {
ReflectionUtils.handleReflectionException(ex);
} catch (NoSuchMethodException ex) {
throw new IllegalStateException(String.format("#CustomScheduled method '%s' found on bean target class '%s', "
+ "but not found in any interface(s) for bean JDK proxy. Either "
+ "pull the method up to an interface or switch to subclass (CGLIB) "
+ "proxies by setting proxy-target-class/proxyTargetClass " + "attribute to 'true'", method.getName(),
targetClass.getSimpleName()));
}
}
Runnable runnable = new ScheduledMethodRunnable(bean, method);
boolean processedSchedule = false;
String errorMessage = "Exactly one of 'cron', 'fixedDelay', or 'fixedRate' is required.";
boolean numberFormatException = false;
String numberFormatErrorMessage = "Delay value is not a number!";
String cron = newScheduledAnnotation.cron();
if (!"".equals(cron)) {
processedSchedule = true;
if (embeddedValueResolver != null) {
cron = embeddedValueResolver.resolveStringValue(cron);
}
cronTasks.put(runnable, cron);
LOG.info("Put cron in tasks map with value {}", cron);
}
// fixedDelay value resolving
Long fixedDelay = null;
String resolverDelayCandidate = newScheduledAnnotation.fixedDelay();
if (!"".equals(resolverDelayCandidate)) {
try {
if (embeddedValueResolver != null) {
resolverDelayCandidate = embeddedValueResolver.resolveStringValue(resolverDelayCandidate);
fixedDelay = Long.valueOf(resolverDelayCandidate);
} else {
fixedDelay = Long.valueOf(newScheduledAnnotation.fixedDelay());
}
} catch (NumberFormatException e) {
numberFormatException = true;
}
}
Assert.isTrue(!numberFormatException, numberFormatErrorMessage);
if (fixedDelay != null && fixedDelay >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
fixedDelayTasks.put(runnable, fixedDelay);
LOG.info("Put fixedDelay in tasks map with value {}", fixedDelay);
}
// fixedRate value resolving
Long fixedRate = null;
String resolverRateCandidate = newScheduledAnnotation.fixedRate();
if (!"".equals(resolverRateCandidate)) {
try {
if (embeddedValueResolver != null) {
fixedRate = Long.valueOf(embeddedValueResolver.resolveStringValue(resolverRateCandidate));
} else {
fixedRate = Long.valueOf(newScheduledAnnotation.fixedRate());
}
} catch (NumberFormatException e) {
numberFormatException = true;
}
}
Assert.isTrue(!numberFormatException, numberFormatErrorMessage);
if (fixedRate != null && fixedRate >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
fixedRateTasks.put(runnable, fixedRate);
LOG.info("Put fixedRate in tasks map with value {}", fixedRate);
}
Assert.isTrue(processedSchedule, errorMessage);
}
}
});
return bean;
}
}
spring-context.xml config file
<beans...>
<!-- Enables the use of a #CustomScheduled annotation-->
<bean class="org.package.CustomScheduledAnnotationBeanPostProcessor" />
</beans>

Some spring annotations support SpEL.
First:
<context:property-placeholder
location="file:${external.config.location}/application.properties" />
And then, for example:
#Value("${delayValue}")
private int delayValue;
I'm not sure if #Scheduled supports SPeL, though, but in general, that's the approach.
In regard to scheduling, check this post of mine and this related question

A better way to do this is to define the scheduling in xml using the task name space
<context:property-placeholder location="scheduling.properties"/>
<task:scheduled ref="someBean" method="someMethod" fixed-delay="${delayValue}"/>
If you for some reason want to do it with annotation, you need to create an annotation that has another optional attribute where you can specify the property name or better still a property placeholder expression or Spel expression.
#MyScheduled(fixedDelayString="${delay}")

If you want to make this work with annotation rather than bean configuration xml, you can use the following annotations: #Component, #PropertySource with PropertySourcesPlaceholderConfigurer Bean itself, like this:
#Component
#PropertySource({ "classpath:scheduling.properties" })
public class SomeClass {
#Scheduled(fixedDelay = "${delay}")
public void someMethod(){
// perform something
}
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}

We can use a field value from other beans. Suppose we have a bean named someBean with a field someValue equal to 10. Then, 10 will be assigned to the field:
#Value("#{someBean.someValue}")
private Integer someBeanValue;
Reference: A Quick Guide to Spring #Value - Baeldung

Related

Integrating javax validation with JavaFX

I have worked on several Spring MVC projects where the validation could be done very simply like such :
CONTROLLER
#RequestMapping(value = {"/newHeightUnit"}, method = RequestMethod.POST)
public String saveHeightUnit(#Valid HeightUnit heightUnit, BindingResult result, ModelMap model)
{
boolean hasCustomErrors = validate(result, heightUnit);
if ((hasCustomErrors) || (result.hasErrors()))
{
setPermissions(model);
return "heightUnitDataAccess";
}
heightUnitService.save(heightUnit);
session.setAttribute("successMessage", "Successfully added height unit \"" + heightUnit.getName() + "\"!");
return "redirect:/heightUnits/list";
}
private boolean validate(BindingResult result, HeightUnit heightUnit)
{
boolean hasCustomErrors = false;
if (heightUnitService.nameExists(heightUnit))
{
FieldError error = new FieldError("heightUnit", "name", heightUnit.getName(), false, null, null,
heightUnit.getName() + " already exists!");
result.addError(error);
hasCustomErrors = true;
}
return hasCustomErrors;
}
This would validate the entity against whatever validation annotation it had (#NotNull, #Size, #Digits, etc).
How can the same be achieved in JavaFX? I have 9 entities all with their validation annotations as I was doing in my MVC projects. I am using Spring with what you could call a view / service / dao structure. I do not use FXML at all, my UI components are all generated in pure Java and I intend for it to stay that way.
How can I use the validation annotations on my entities in a similarly friendly approach to that of Spring MVC?
Clarifications
Just for reference, this is how my entities are currently saved. There is currently no validation of the user's inputs whatsoever when they are added but everything works perfectly fine. My entities are all annotated and ready to go and i'm just looking to learn how to integrate the good ol' #Valid into the mix:
#Override
public void saveEntity()
{
TextField nameField = (TextField)formFields.get(0);
try
{
Category newCategory = new Category(null, nameField.getText(), new Date(), null);
categoryService.save(newCategory);
}
catch (Exception ex)
{
logger.error("Error adding category : " + ex.getMessage());
}
}
Thanks!
So i ended up with a pretty clean result. First off i ended up with a validator class that looks something like this :
public class EntityValidator
{
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
public Set<ConstraintViolation<Category>> validateCategory(Category category)
{
return validator.validate(category);
}
}
I am using Spring to make this class available for autowiring :
#Bean
public EntityValidator entityValidator()
{
return new EntityValidator();
}
The bean validation goes something like this :
TextField nameField = (TextField)formFields.get(0);
try
{
Category newCategory = new Category(null, nameField.getText(), new Date(), null);
Set<ConstraintViolation<Category>> errors = validator.validateCategory(newCategory);
if (errors.isEmpty())
{
categoryService.save(newCategory);
close();
}
else
{
showErrorMessages(errors);
}
}
catch (Exception ex)
{
logger.error("Error adding category : " + ex.getMessage());
}
The showErrorMessages method just takes the error Set and displays the first error in an error dialog. Since i am using validation groups, there is never more than one error in the Set so this all looks pretty clean. It will never be as simple as doing it from a controller in a web project but i'm pretty happy with the result overall.
Cheers

Spring Retry does not work on 2nd level of methods

#Retryable doesn't seem to be working on 2nd level of methods as in sphRemoteCall below. I see that a proxy is created but it is never retried on failures.
Once I moved #Retryable to the 1st level of methods like getSubscriberAccount, it's started working.
Example below:
#Service
public class SphIptvClient extends WebServiceGatewaySupport {
//Works over here
#Retryable(maxAttempts=3, backoff=#Backoff(delay=100))
public GetSubscriberAccountResponse getSubscriberAccount(String loginTocken, String billingServId) {
GetSubscriberAccountResponse response = (GetSubscriberAccountResponse) sphRemoteCall(sphIptvEndPoint, getSubAcc, "xxxxx");
return response;
}
/*
* Retryable is not working on the 2nd level methods in the bean.
* It works only with methods which are called directly from outside
* if there is 2nd level method, like this, Retryable is not working.
*/
//#Retryable
private Object sphRemoteCall(String uri, Object requestPayload, String soapAction) {
log.debug("Calling the sph for uri:{} and soapAction:{}", uri, soapAction);
return getWebServiceTemplate().marshalSendAndReceive(uri, requestPayload, new SoapActionCallback(soapAction));
}
}
#Configuration
#EnableRetry
public class SphClientConfig {
#Bean
public SphIptvClient sphIptvClient() {
SphIptvClient client = new SphIptvClient();
return client;
}
}
So this is a super late answer, but since I've just come here and confronted the same problem (again, after years ago wrestling with transactions) I'll furnish a little more fleshed out solution and hopefully someone will find it useful. Suffice to say that #M. Deinum's diagnosis is correct.
In the above case, and to paraphrase Understanding AOP proxies, any place where SphIptvClient gets autowired will be given a reference to a proxy which Spring Retry will create when #EnableRetry is handled:
"The #EnableRetry annotation creates proxies for #Retryable beans" - Declarative Retry - Spring Retry
Once getSubscriberAccount has been invoked and execution has passed through the proxy and into the #Service instance of the object, no reference to the proxy is known. As a result sphRemoteCall is called as if there were no #Retryable at all.
You could work with the framework by shuffling code around in such a way as to allow getSubscriberAccount to call a proxy-ed sphRemoteCall, which requires a new interface and class implementation.
For example:
public interface SphWebService {
Object sphRemoteCall(String uri, Object requestPayload, String soapAction);
}
#Component
public class SphWebServiceImpl implements SphWebService {
#Retryable
public Object sphRemoteCall(String uri, Object requestPayload, String soapAction) {
log.debug("Calling the sph for uri:{} and soapAction:{}", uri, soapAction);
return getWebServiceTemplate().marshalSendAndReceive(uri, requestPayload, new SoapActionCallback(soapAction));
}
}
#Service
public class SphIptvClient extends WebServiceGatewaySupport {
#Autowired
SphWebService sphWebService;
#Retryable(maxAttempts=3, backoff=#Backoff(delay=100))
public GetSubscriberAccountResponse getSubscriberAccount(String loginTocken, String billingServId) {
GetSubscriberAccountResponse response = (GetSubscriberAccountResponse) this.sphWebService.sphRemoteCall(sphIptvEndPoint, getSubAcc, "xxxxx");
return response;
}
}
#Configuration
#EnableRetry
public class SphClientConfig {
// the #Bean method was unnecessary and may cause confusion.
// #Service was already instantiating SphIptvClient behind the scenes.
}
#Retryable only works on the methods when called directly from other classes.
If you will try to invoke one method with #Retryable annotation from some other method of the same class, it will eventually not work.
// any call from this method to test method will not invoke the retry logic.
public void yetAnotherMethod() {
this.test();
}
// it will work
#Retryable(value = {RuntimeException.class}, backoff = #Backoff(delay = 1500))
public void test() {
System.out.println("Count: " + count++);
throw new RuntimeException("testing");
}
#Recover
public void recover() {
System.out.println("Exception occured.");
}
So, the output if test method is called, will be:
Count: 0
Count: 1
Count: 2
Exception occured.
But, if the yetAnotherMethod is called, output will be:
Count: 0
And a Runtime exception will be thrown.
Suppose you have a method which calls certain API - callAPI() and you want to implement retry logic over it, you can try use a do while, as it will execute only once, if successful.
Method to hit the external API
public int callAPI() {
return 1;
}
Method to implement retry logic
public int retrylogic() throws InterruptedException {
int retry = 0;
int status = -1;
boolean delay = false;
do {
// adding a delay, if you want some delay between successive retries
if (delay) {
Thread.sleep(2000);
}
// Call the actual method, and capture the response,
// and also catch any exception which occurs during the call.
// (Network down/ endpoint not avaliable
try {
status = callAPI();
}
catch (Exception e) {
System.out.println("Error occured");
status = -1;
}
finally {
switch (status) { //now based on error response or any exception you retry again
case HTTPStatus.OK:
System.out.println("OK");
return status;
default:
System.out.println("Unknown response code");
break;
}
retry++;
System.out.println("Failed retry " + retry + "/" + 3);
delay = true;
}
}while (retry < 3);
return status;
}

SELECT FOR UPDATE query with Spring's #Transaction management creates deadlock upon subsequent updates

Setup: Spring application deployed on Weblogic 12c, using JNDI lookup to get a datasource to the Oracle Database.
We have multiple services which will be polling the database regularly for new jobs. In order to prevent two services picking the same job we are using a native SELECT FOR UPDATE query in a CrudRepository. The application then takes the resulting job and updates it to PROCESSING instead of WAITING using the CrusRepository.save() method.
The problem is that I can't seem to get the save() to work within the FOR UPDATE transaction (at least this is my current working theory of what goes wrong), and as a result the entire polling freezes until the default 10 minute timeout occurs. I have tried putting #Transactional (with various propagation flags) basically everywhere, but I'm not able to get it to work (#EnableTransactionManagement is activated and working).
Obviously there must be some basic knowledge I'm missing. Is this even a possible setup? Unfortunately, just using #Transactional with a non-native CrudRepository SELECT query is not possible, as it apparently first makes a SELECT to see if the row is locked or not, and only then makes a new SELECT that locks it. Another service could very well pick up the same job in the meanwhile, which is why we need it to lock immediately.
Update in relation to #M. Deinum's comment.: I should perhaps also mention that it's a setup wherein the central component that's doing the polling is a library used by all the other services (therefore the library has #SpringBootApplication, as does each service using it, so double component scanning is certainly present). Furthermore, the service has two separate classes for polling depending on the type of service, with a lot of common code, shared in an AbstractTransactionHelper class. Below I've aggregated some code for the sake of brevity.
The library's main class:
#SpringBootApplication
#EnableTransactionManagement
#EnableJpaRepositories
public class JobsMain {
public static void initializeJobsMain(){
PersistenceProviderResolverHolder.setPersistenceProviderResolver(new PersistenceProviderResolver() {
#Override
public List<PersistenceProvider> getPersistenceProviders() {
return Collections.singletonList(new HibernatePersistenceProvider());
}
#Override
public void clearCachedProviders() {
//Not quite sure what this should do...
}
});
}
#Bean
public JtaTransactionManager transactionManager(){
return new WebLogicJtaTransactionManager();
}
public DataSource dataSource(){
final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
DataSource dataSource = dsLookup.getDataSource("Jobs");
return dataSource;
}
}
The repository (we're returning a set with only one job as we had some other issues when returning a single object):
public interface JobRepository extends CrudRepository<Job, Integer> {
#Query(value = "SELECT * FROM JOB WHERE JOB.ID IN "
+ "(SELECT ID FROM "
+ "(SELECT * FROM JOB WHERE "
+ "JOB.STATUS = :status1 OR "
+ "JOB.STATUS = :status2 "
+ "ORDER BY JOB.PRIORITY ASC, JOB.CREATED ASC) "
+ "WHERE ROWNUM <= 1) "
+ "FOR UPDATE", nativeQuery = true)
public Set<Job> getNextJob(#Param("status1") String status1, #Param("status2") String status2);
The transaction handling class:
#Service
public class JobManagerTransactionHelper extends AbstractTransactionHelper{
#Transactional
#Override
public QdbJob getNextJobToProcess(){
Set<Job> jobs = null;
try {
jobs = jobRepo.getNextJob(Status.DONE.name(), Status.FAILED.name());
} catch (Exception ex) {
logger.error(ex);
}
return extractSingleJobFromSet(jobs);
}
Update 2: Some more code.
AbstractTransactionHelper:
#Service
public abstract class AbstractTransactionHelper {
#Autowired
QdbJobRepository jobRepo;
#Autowired
ArchivedJobRepository archive;
protected Job extractSingleJobFromSet(Set<Job> jobs){
Job job = null;
if(jobs != null && !jobs.isEmpty()){
for(job job : jobs){
if(this instanceof JobManagerTransactionHelper){
updateJob(job);
}
job = job;
}
}
return job;
}
protected void updateJob(Job job){
updateJob(job, Status.PROCESSING, null);
}
protected void updateJob(Job job, Status status, String serviceMessage){
if(job != null){
if(status != null){
job.setStatus(status);
}
if(serviceMessage != null){
job.setServiceMessage(serviceMessage);
}
saveJob(job);
}
}
protected void saveJob(Job job){
jobRepo.save(job);
archive.save(Job.convertJobToArchivedJob(job));
}
Update 4: Threading. newJob() is implemented by each service that uses the library.
#Service
public class JobManager{
#Autowired
private JobManagerTransactionHelper transactionHelper;
#Autowired
JobListener jobListener;
#Autowired
Config config;
protected final AtomicInteger atomicThreadCounter = new AtomicInteger(0);
protected boolean keepPolling;
protected Future<?> futurePoller;
protected ScheduledExecutorService pollService;
protected ThreadPoolExecutor threadPool;
public boolean start(){
if(!keepPolling){
ThreadFactory pollServiceThreadFactory = new ThreadFactoryBuilder()
.setNamePrefix(config.getService() + "ScheduledPollingPool-Thread").build();
ThreadFactory threadPoolThreadFactory = new ThreadFactoryBuilder()
.setNamePrefix(config.getService() + "ThreadPool-Thread").build();
keepPolling = true;
pollService = Executors.newSingleThreadScheduledExecutor(pollServiceThreadFactory);
threadPool = (ThreadPoolExecutor)Executors.newFixedThreadPool(getConfig().getThreadPoolSize(), threadPoolThreadFactory);
futurePoller = pollService.scheduleWithFixedDelay(getPollTask(), 0, getConfig().getPollingFrequency(), TimeUnit.MILLISECONDS);
return true;
}else{
return false;
}
}
protected Runnable getPollTask() {
return new Runnable(){
public void run(){
try{
while(atomicThreadCounter.get() < threadPool.getMaximumPoolSize() &&
threadPool.getActiveCount() < threadPool.getMaximumPoolSize() &&
keepPolling == true){
Job job = transactionHelper.getNextJobToProcess();
if(job != null){
threadPool.submit(getJobHandler(job));
atomicThreadCounter.incrementAndGet();//threadPool.getActiveCount() isn't updated fast enough the first loop
}else{
break;
}
}
}catch(Exception e){
logger.error(e);
}
}
};
}
protected Runnable getJobHandler(final Job job){
return new Runnable(){
public void run(){
try{
atomicThreadCounter.decrementAndGet();
jobListener.newJob(job);
}catch(Exception e){
logger.error(e);
}
}
};
}
As it turns out, the problem was the WeblogicJtaTransactionManager. My guess is that the FOR UPDATE resulted in a JPA transaction, but upon updating the object in the database, the WeblogicJtaTransactionManager was used, which failed to find an ongoing JTA transaction. Since we're deploying on Weblogic we wrongly assumed we had to use the WeblogicJtaTransactionManager.
Either way, exchanging the TransactionManager to a JpaTransactionManager (and explicitly setting the EntityManagerFactory and DataSource on it) basically solved all problems.
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(entityManagerFactory().getObject());
jpaTransactionManager.setDataSource(dataSource());
jpaTransactionManager.setJpaDialect(new HibernateJpaDialect());
return jpaTransactionManager;
}
Assuming you also have added an EntityManagerFactoryBean which is needed if you want to use multiple datasources in the same project (which we're doing, but not within single transactions, so no need for JTA).
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setJpaVendorAdapter(vendorAdapter);
factoryBean.setPackagesToScan("my.model");
return factoryBean;
}

Checking if class is proxified with CDI 1.2

In CDI 1.2 there is a way to check if a class instance is proxified? I need this because I need to get the name of original class, not the proxy name.
#Inject Bean bean;
public void sysout() {
// will print something like com.Bean$$Weld9239823
System.out.println(bean.getClass());
// I don't know how to check if the bean instance if a proxy or real class instance
}
Using Weld classes I can do this job:
public void sysout() {
// will print true because this is a proxy
System.out.println(ProxyObject.class.isAssignableFrom(bean));
// will print com.Bean
System.out.println(((TargetInstanceProxy) bean).getTargetInstance());
}
In CDI 1.1 there is no method to do this. I search inside CDI 1.2 docs if a method was added about this, but I don't found anything.
So... I miss something and CDI 1.2 there is a method to get original class name and instance? Or if not, there is a plain to add this feature in near feature?
For Weld on WildFly do this:
public boolean isProxy(Object obj) {
try{
return Class.forName("org.jboss.weld.bean.proxy.ProxyObject").isInstance(obj);
} catch (Exception e) {
log.error("Unable to check if object is proxy", e);
}
return false;
}
To retrive actual object instead of proxy (I need to serialize it) I do this:
public Object getObject(Object obj) {
Field f = null;
boolean isAccessible = false;
try {
for(Field fi : Class.forName(handler).getDeclaredFields()) {
if(fi.getName().equals(field)) {
f = fi;
isAccessible = f.isAccessible();
f.setAccessible(true);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
if(f == null) {
throw new RuntimeException(new NoSuchFieldException(String.format(
"The required field '%s' not found in '%s'. " +
"May be the code is obsolete for running on this application server.",
field, method)));
} else {
try{
obj = f.get(getHandler(obj));
for(Method m : Class.forName(instance).getMethods()) {
if(m.getName().equals(value)) {
return m.invoke(obj);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
f.setAccessible(isAccessible);
}
throw new NoSuchMethodError(String.format(
"The required method '%s' not found in '%s'. " +
"May be the code is obsolete for running on this application server.",
value, instance));
}
}
Be aware, that it is the darkest magic as possible, have very poor performance and can break at any WildFly update, if they change classes, methods for fields in it.
This is a terrible hack, but for Weld (and possibly other implementations) you can check if the class name contains "Proxy": possibleProxy.getClass().getSimpleName().contains("Proxy"). I use it only for logging purposes to get a cleaned up version of the wrapped class name:
/**
* Get the actual simple name of the objects class that might be wrapped by
* a proxy. A "simple" class name is not fully qualified (no package name).
*
* #param possibleProxy an object that might be a proxy to the actual
* object.
* #return the simple name of the actual object's class
*/
public static String getActualSimpleClassName(final Object possibleProxy) {
final String outerClassName = possibleProxy.getClass().getSimpleName();
final String innerClassName;
if (outerClassName.contains("Proxy")) {
innerClassName = outerClassName.substring(0, outerClassName.indexOf('$'));
} else {
innerClassName = outerClassName;
}
return innerClassName;
}
you can make a method inside your proxied cdi bean like
public String getClassName() {
return this.getClass().getName();
}
this is not the best solution, but a simple pragmatic way to get the class name through the proxy... the downside of this is that the method must be on every implementation...

Is it possible automatically instantiation of a nested Property with Commons Bean Utils?

I'm using PropertyUtils.setProperty(object, name, value) method of Apache Commons Bean Utils:
Giving these classes:
public class A {
B b;
}
public class B {
C c;
}
public class C {
}
And this:
A a = new A();
C c = new C();
PropertyUtils.setProperty(a, "b.c", c); //exception
If I try that I get:
org.apache.commons.beanutils.NestedNullException: Null property value for 'b.c' on bean class 'class A'
Is it possible to tell PropertyUtils that if a nested property has a null value try to instantiate it (default constructor) before trying to go deeper?
Any other approach?
Thank you
I solved it by doing this:
private void instantiateNestedProperties(Object obj, String fieldName) {
try {
String[] fieldNames = fieldName.split("\\.");
if (fieldNames.length > 1) {
StringBuffer nestedProperty = new StringBuffer();
for (int i = 0; i < fieldNames.length - 1; i++) {
String fn = fieldNames[i];
if (i != 0) {
nestedProperty.append(".");
}
nestedProperty.append(fn);
Object value = PropertyUtils.getProperty(obj, nestedProperty.toString());
if (value == null) {
PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(obj, nestedProperty.toString());
Class<?> propertyType = propertyDescriptor.getPropertyType();
Object newInstance = propertyType.newInstance();
PropertyUtils.setProperty(obj, nestedProperty.toString(), newInstance);
}
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
}
I know the question is about apache commons PropertyUtils.setProperty but there is very similar functionality available in
Spring Expression Language "SpEL" which does exactly what you want. Better still it deals with lists and arrays too. The doc link above is for spring 4.x but the code below works for me in spring 3.2.9.
StockOrder stockOrder = new StockOrder(); // Your root class here
SpelParserConfiguration config = new SpelParserConfiguration(true,true); // auto create objects if null
ExpressionParser parser = new SpelExpressionParser(config);
StandardEvaluationContext modelContext = new StandardEvaluationContext(stockOrder);
parser.parseExpression("techId").setValue(modelContext, "XXXYYY1");
parser.parseExpression("orderLines[0].partNumber").setValue(modelContext, "65498");
parser.parseExpression("orderLines[0].inventories[0].serialNumber").setValue(modelContext, "54686513216");
System.out.println(ReflectionToStringBuilder.toString(stockOrder));
A little correction:
String fn = fieldNames[i];
if (i != 0) {
nestedProperty.append(".");
}
nestedProperty.append(fn);
Object value = PropertyUtils.getProperty(obj, nestedProperty.toString());
IMHO, the best solution is to get rid of the commons-beanutils and use Spring Framework org.springframework.beans.PropertyAccessorFactory
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(targetObject);
wrapper.setAutoGrowNestedPaths(true);
I won't delve into details of how it works, but if you want to check it out, go take a look at the link above, this API is quite intuitive, but you'll need to have Spring Framework Core configured on your classpath, so I wouldn't recommend that you add spring just for the sake of this feature.
However,
If you only have commons-beanutils as your ally, this following code snippet may help you to grow your nested paths, as you set the values, therefore, you won't need to concern about the null objects along the path properties.
In this example I used with JPA Tuple Query to construct a custom object with some specific property paths with its corresponding values to be set.
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.beanutils.expression.DefaultResolver;
public class TupleToObject<T> {
public List<T> transformResult(List<Tuple> result, Class<T> targetClass) {
try {
List<T> objects = new ArrayList<>();
for (Tuple tuple : result) {
T target = targetClass.newInstance();
List<TupleElement<?>> elements = tuple.getElements();
for (TupleElement<?> tupleElement : elements) {
String alias = tupleElement.getAlias();
Object value = tuple.get(alias);
if (value != null) {
instantiateObject(target, alias);
PropertyUtils.setNestedProperty(target, alias, value);
}
}
objects.add(target);
}
return objects;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void instantiateObject(T target, String propertyPath) throws Exception {
DefaultResolver resolver = new DefaultResolver();
Object currentTarget = target;
while (resolver.hasNested(propertyPath)) {
final String property = resolver.next(propertyPath);
Object value = PropertyUtils.getSimpleProperty(currentTarget, property);
if (value == null) {
Class<?> propertyType = PropertyUtils.getPropertyType(currentTarget, property);
value = propertyType.newInstance();
PropertyUtils.setSimpleProperty(currentTarget, property, value);
}
currentTarget = value;
propertyPath = resolver.remove(propertyPath);
}
}
}
This code is using commons-beanutils-1.9.3.jar
Hope it helps!
I have used only reflection w/o Apache library to achieve this. The assumption is that all object to be traversed are all POJOs, and default construction is publicly accessible. This way, there is no need to construct the reference path for each loop.
public Object getOrCreateEmbeddedObject(Object inputObj,String[] fieldNames) throws Exception {
Object cursor = inputObj;
//Loop until second last index
for (int i = 0; i < fieldNames.length - 1; i++){
Field ff = getClassFieldFrom(cursor,fieldNames[i]);
Object child = ff.get(cursor);
if(null == child) {
Class<?> cls=ff.getType();
child = cls.newInstance();
ff.set(cursor, child);
}
cursor = child;
}
return cursor;
}
private Field getClassFieldFrom(Object object, String fieldStr)
throws NoSuchFieldException {
java.lang.reflect.Field ff = object.getClass().getDeclaredField(fieldStr);
ff.setAccessible(true);
return ff;
}
If you have any suggestion to my solution , please let me know.
I went for the very basic approach of just instantiating each of the objects by default:
public class A {
B b = new B();
}
public class B {
C c = new C();
}
public class C {
}
Not ideal, but it worked for my situation and didn't involve complicated fixes.
After doing some research, the short answer to "Is it possible..." question is No.

Categories

Resources