How to get method arguments using reflection - spring

This method is first checked by beanDefintirions for the original Beans (Configureablelistablebeanfactory beanFactory is used; ).
ConfigurableListableBeanFactory beanFactory; injected.
Then all the methods of the original bean, which was obtained from the BeanFactory, are iterated over. After searching over the methods of a certain annotation, we get the Bean from the Applicationcontext.
This Bean is a proxy wrapper over the original Bean, which was formed at the - > postProcessBeforeInitialization() stage. Now through this bean, I call a method that has been marked with the annotation I need, but it requires another argument Obj ..args.
How do I get the missing argument ?
Использую Srping 5.x, java 11
private void runMethodWithPostProxyThirdPhaseAnnotation(String beanName, ApplicationContext applicationContext) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
try {
String originalBeanClassName = beanDefinition.getBeanClassName();
if (originalBeanClassName != null) {
Class<?> originalClass = Class.forName(originalBeanClassName);
Method[] methods = originalClass.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(PostProxyThirdPhase.class)) {
String originalMethodName = method.getName();
Class<?>[] parameterTypesFromOriginalMethod = method.getParameterTypes();
Object beanAfterProxy = applicationContext.getBean(beanName);
Method methodFromProxyBean = beanAfterProxy
.getClass()
.getMethod(originalMethodName, parameterTypesFromOriginalMethod);
methodFromProxyBean.invoke(beanAfterProxy, ?);
}
}
}
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}

Solution
three-phase constructor (implementation).
The answer was in the documentation for the method :
public Object invoke(Object obj, Object... args)
#param args the arguments used for the method call
import bean.post.process.annotation.PostProxyThirdPhase;
import bean.post.process.bean.Quoter;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
#Component
public class PostProxyInvokerContextListener implements ApplicationListener<ContextRefreshedEvent> {
private final ConfigurableListableBeanFactory beanFactory;
private final Quoter quoter;
public PostProxyInvokerContextListener(ConfigurableListableBeanFactory beanFactory, Quoter quoter) {
this.beanFactory = beanFactory;
this.quoter = quoter;
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
Arrays.stream(beanDefinitionNames)
.forEach(beanName -> runMethodWithPostProxyThirdPhaseAnnotation(beanName, applicationContext));
}
private void runMethodWithPostProxyThirdPhaseAnnotation(String beanName, ApplicationContext applicationContext) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
try {
String originalBeanClassName = beanDefinition.getBeanClassName();
if (originalBeanClassName != null) {
Class<?> originalClass = Class.forName(originalBeanClassName);
Method[] methods = originalClass.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(PostProxyThirdPhase.class)) {
String originalMethodName = method.getName();
Class<?>[] parameterTypesFromOriginalMethod = method.getParameterTypes();
Object beanAfterProxy = applicationContext.getBean(beanName);
Method methodFromProxyBean = beanAfterProxy
.getClass()
.getMethod(originalMethodName, parameterTypesFromOriginalMethod);
Object[] args = new Object[]{quoter};
methodFromProxyBean.invoke(beanAfterProxy, args);
}
}
}
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}

Related

NullPointerException when using customized Autowired

I customized a Annotation #CustomizedAutowired like #Autowired by using BeanPostProcessor (InjectBeanPostProcessor.java), but I got a NullPointerException when AOP is used.
Why it is null when using AOP?
Why DemoController seems to be proxied twice when using AOP?
what should I do, so that #CustomizedAutowired can work just like #Autowired?
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#Inherited
public #interface CustomizedAutowired {}
#RestController
#RequestMapping("/hello")
public class DemoController {
#CustomizedAutowired
private InjectBean injectBean;
#GetMapping("/world")
public LocalDateTime hello() {
injectBean.hello(); // injectBean is null
return LocalDateTime.now();
}
}
#Aspect
#Component
public class AopDemo {
#Pointcut("execution(public java.time.LocalDateTime *(..))")
public void pointcut() {}
#AfterReturning(pointcut = "pointcut()")
public void round() {
System.out.println("after returning");
}
}
#Component
public class InjectBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?> targetClass = bean.getClass();
while (targetClass != null) {
Field[] fields = targetClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(CustomizedAutowired.class)) {
field.setAccessible(true);
try {
field.set(bean, new InjectBean());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
targetClass = targetClass.getSuperclass();
}
return bean;
}
}
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#CustomizedAutowired
private InjectBean injectBean;
#Override
public void run(String... args) throws Exception {
System.out.println("instance -> " + this);
injectBean.hello(); // works fine here
}
}
Here is the result:
You cannot do field.set(bean, new InjectBean()) on the proxy bean, because it does not inherit any private fields. You need to unwrap the proxy and set the field on the original object.
I am not going to comment on the idea of using all that ugly reflection in order to implement your custom injection idea, just help you make it work. You can use AopTestUtils.getTargetObject(bean) instead of your while-loop in order to get the original object and then easily its class afterwards.
How about this?
package de.scrum_master.spring.q70408968;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.test.util.AopTestUtils;
import java.lang.reflect.Field;
#Component
public class InjectBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// Unwrap bean, in case it is a proxy
Object targetObject = AopTestUtils.getTargetObject(bean);
Class<?> targetClass = targetObject.getClass();
Field[] fields = targetClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(CustomizedAutowired.class)) {
field.setAccessible(true);
try {
field.set(targetObject, new InjectBean());
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return bean;
}
}
This is going to work both with and without AOP proxies. Note how I am injecting the field into targetObject, but returning the original bean instance (i.e. the proxy in the AOP case).
Then, get rid of the annotated member in the application class, because the application is not a normal Spring component.
package de.scrum_master.spring.q70408968;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
try (ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args)) {
context.getBean(DemoController.class).hello();
}
}
}
Now the application runs just fine.
o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
d.s.spring.q70408968.DemoApplication : Started DemoApplication in 7.457 seconds (JVM running for 10.318)
Hello from InjectBean
after returning
Or maybe you prefer a Java streams approach, but that is just cosmetics:
package de.scrum_master.spring.q70408968;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.test.util.AopTestUtils;
import java.util.Arrays;
#Component
public class InjectBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// Unwrap bean, in case it is a proxy
Object targetObject = AopTestUtils.getTargetObject(bean);
Arrays.stream(targetObject.getClass().getDeclaredFields())
.filter(field -> field.isAnnotationPresent(CustomizedAutowired.class))
.forEach(field -> {
field.setAccessible(true);
try {
field.set(targetObject, new InjectBean());
}
catch (IllegalAccessException e) { e.printStackTrace(); }
});
return bean;
}
}
After searching from google, I found that the reason why NPE happens is that we got the wrong targetObject, BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName) gives us a proxied bean, and if we use AOP, it will be proxied twice, replace postProcessAfterInitialization with postProcessBeforeInitialization can solve this problem, or another solution that can do the Injection Operation before being proxied by AOP.

AWS lambda spring . Not able to load properties file

I have problem with loading the properties file where i reference the values from. Locally I am able to run it as expected. But AWS lambda function does not seem to work as expected since it is not able to laod properties file. Below is the handler written. I deploy MainHanlder.java function on lambda.
#Component
public class TestHandler implements RequestHandler<SNSEvent, Object> {
#Override
public String handleRequest(SNSEvent snsEvent, Context context) {
TestClient testClient = Application.getBean("pp",TestClient);
return null;
}
}
MainHandler.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.amazonaws.services.lambda.runtime.events.SNSEvent;
import com.test.lambda.ApplicationConfiguration;
public class MainHandler extends SpringRequestHandler<SNSEvent, Object> {
/**
* Here we create the Spring {#link ApplicationContext} that will
* be used throughout our application.
*/
private static final ApplicationContext context =
new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
#Override
public ApplicationContext getApplicationContext() {
return context;
}
}
SpringRequestHandler.java
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import org.springframework.context.ApplicationContext;
#SuppressWarnings("unchecked")
public abstract class SpringRequestHandler<I, O> implements RequestHandler<I, O>, ApplicationContextProvider {
private final RequestHandler<I, O> handler;
public SpringRequestHandler() {
handler = getApplicationContext().getBean(RequestHandler.class);
}
#Override
public O handleRequest(final I input, final Context context) {
return (O) handler.handleRequest(input, context);
}
}
Application.java
public class Application {
private static final AnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext();
private static boolean flag = Boolean.TRUE;
private static final XLogger logger = XLoggerFactory.getXLogger(Application.class);
public static <T> T getBean(String env, Class<T> clazz) {
InputStream i1 = null;
InputStream i2 = null;
if(flag) {
Properties rp = new Properties();
Properties ap = new Properties();
try {
System.out.println("print env " + env);
i1 = Application.class.getResourceAsStream(“/application-” + env + ".properties");
rp.load(i1);
i2 = Application.class.getResourceAsStream("/application-" + env + ".properties");
ap.load(i2);
PropertyPlaceholderConfigurer propertyOverrideConfigurer = new PropertyPlaceholderConfigurer();
propertyOverrideConfigurer.setPropertiesArray(new Properties[]{rp,ap});
springContext.scan(new String[]{"com.pinto.lambda"});
springContext.addBeanFactoryPostProcessor(propertyOverrideConfigurer);
try {
springContext.refresh();
}catch(IllegalStateException e) {
}
flag = Boolean.FALSE;
} catch (Exception e) {
logger.error("Exception in the Application - " +e.getMessage());
throw new RuntimeException("Unable to load properties " + e.getMessage());
}
return springContext.getBean(clazz);
}
ERROR TRACE - AWS CONSOLE LOGS
==================== FUNCTION OUTPUT ====================
{"errorMessage":"Unable to load properties null","errorType":"java.lang.RuntimeException","stackTrace":["com.pinto.lambda.Application.getBean(Application.java:65)","com.pinto.lambda.handler.GetWarehouseInventoryHandler.handleRequest(TestHandler.java:44)","com.pinto.lambda.handler.GetWarehouseInventoryHandler.handleRequest(TestHandler.java:1)","com.pinto.lambda.handler.SpringRequestHandler.handleRequest(SpringRequestHandler.java:19)"]}
==================== FUNCTION LOG OUTPUT ====================
print env pp
[ERROR] Exception in the Application - null
Unable to load properties null: java.lang.RuntimeException
java.lang.RuntimeException: Unable to load properties null
at com.pinto.lambda.Application.getBean(Application.java:65)
at com.pinto.lambda.handler.TestHandler.handleRequest(TestHandler.java:44)
at com.pinto.lambda.handler.TestHandler.handleRequest(TestHandler.java:1)
at com.pinto.lambda.handler.SpringRequestHandler.handleRequest(SpringRequestHandler.java:19)

Spring equivalent for CDI Instance

I am new to Spring and i need to convert a CDI class to Spring.
I have the below code in CDI
#Inject
private Instance<HealthCheck> healthChecks;
I then iterate over healthChecks.
I found a similar question What is the Spring equivalent for CDI's Instance, or Guices Provider , where it was advised to use
#Inject
Provider<MyObject> myObjectInstance;
//...
MyObject myObjectInstance.get();
However, since provider does not implements iterable, I am not able to iterate.
Can someone please help me on how can I convert the CDI code block to spring.
create a new file spring.factories inside META-INF with following content:
org.springframework.context.ApplicationContextInitializer=package_name.CustomApplicationContextInitializer
or you can use it in your junit test like:
#SpringApplicationConfiguration(initializers = CustomApplicationContextInitializer.class)
And now you can use like:
#Autowired
private Instance<HealthCheck> healthChecks;
CustomApplicationContextInitializer.class
public class CustomApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// Configure custom CustomAutowireCandidateResolver to handle CDI
// Instance<T> dependency requests
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
beanFactory.setAutowireCandidateResolver(new CustomAutowireCandidateResolver());
}
}
CustomAutowireCandidateResolver.class
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Priority;
import javax.enterprise.inject.Instance;
import javax.enterprise.util.TypeLiteral;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.core.annotation.Order;
import org.springframework.util.ClassUtils;
public class CustomAutowireCandidateResolver extends ContextAnnotationAutowireCandidateResolver {
static final boolean IS_CDI_INSTANCE_CLASS_PRESENT = ClassUtils.isPresent("javax.enterprise.inject.Instance", null);
#Override
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
if (IS_CDI_INSTANCE_CLASS_PRESENT && Instance.class.equals(descriptor.getDependencyType())) {
// TODO refactor getLazyResolutionProxyIfNecessary to allow to
// customize lazy dependency resolution for Instance<T>
return getInstanceAdapterFor(descriptor);
}
return super.getLazyResolutionProxyIfNecessary(descriptor, beanName);
}
#SuppressWarnings({ "unchecked", "rawtypes" })
private Object getInstanceAdapterFor(DependencyDescriptor descriptor) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) getBeanFactory();
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) listableBeanFactory;
// Instance<TargetType>
Class targetType = descriptor.getResolvableType().getGeneric(0).getRawClass();
Map<String, Object> beansOfType = listableBeanFactory.getBeansOfType(targetType);
List<Bean> beansInstances = beansOfType.entrySet().stream() //
.map(e -> new Bean(e.getValue(), registry.getBeanDefinition(e.getKey()).isPrimary()))//
.collect(Collectors.toList());
Annotation[] qualifiers = retainQualifierAnnotations(descriptor.getAnnotations());
Beans beans = new Beans(targetType, beansInstances);
return qualifiers.length == 0 ? beans : beans.select(qualifiers);
}
private Annotation[] retainQualifierAnnotations(Annotation[] annotations) {
return Arrays.stream(annotations) //
.filter(a -> a.annotationType().isAnnotationPresent(Qualifier.class)) //
.toArray(Annotation[]::new);
}
static class Beans<T> implements Instance<T> {
private final List<Bean> beans;
private final Class<?> type;
public Beans(Class<?> type, List<Bean> beans) {
this.type = type;
this.beans = beans;
}
protected List<Bean> getBeans() {
return beans;
}
#Override
public T get() {
return (T) findDefaultInstance();
}
protected Object findDefaultInstance() {
List<Bean> beans = getBeans();
if (beans.size() == 1) {
return beans.get(0).getInstance();
}
Object highestPrioBean = returnPrimaryOrHighestPriorityBean(beans);
if (highestPrioBean != null) {
return highestPrioBean;
}
// TODO figure out a sane default to use here - maybe throw an
// exception?
return beans.get(0).getInstance();
}
private Object returnPrimaryOrHighestPriorityBean(List<Bean> beans) {
long highestPriority = Integer.MIN_VALUE;
Object highestPrioBean = null;
for (Bean bean : beans) {
if (bean.isPrimary()) {
return bean.getInstance();
}
// TODO figure out to retrieve order from BeanDefinition /
// BeanDeclaration
Object instance = bean.getInstance();
Order order = instance.getClass().getAnnotation(Order.class);
if (order != null) {
if (order.value() > highestPriority) {
highestPriority = order.value();
highestPrioBean = instance;
}
}
Priority priority = instance.getClass().getAnnotation(Priority.class);
if (priority != null) {
if (priority.value() > highestPriority) {
highestPriority = priority.value();
highestPrioBean = instance;
}
}
}
return highestPrioBean;
}
#Override
#SuppressWarnings("unchecked")
public Instance<T> select(Annotation... qualifiers) {
return select((Class<T>) type, qualifiers);
}
#Override
public <U extends T> Instance<U> select(Class<U> subtype, Annotation... qualifiers) {
return new Beans<U>(subtype, filterBeans(subtype, qualifiers));
}
protected List<Bean> filterBeans(Class<?> subtype, Annotation... qualifiers) {
List<Annotation> requiredQualifiers = Arrays.asList(qualifiers);
return getBeans().stream() //
.filter(bean -> subtype.isInstance(bean.getInstance())) //
.filter(bean -> bean.getAnnotations().containsAll(requiredQualifiers)) //
.collect(Collectors.toList());
}
#Override
public <U extends T> Instance<U> select(TypeLiteral<U> subtype, Annotation... qualifiers) {
// TODO implement (Class<U> subtype, Annotation... qualifiers) via
// select(TypeLiteral<U> subtype, Annotation... qualifiers)
return select(subtype.getRawType(), qualifiers);
}
#Override
public Iterator<T> iterator() {
return getBeans().stream().map(bean -> (T) bean.getInstance()).iterator();
}
#Override
public boolean isUnsatisfied() {
return getBeans().isEmpty();
}
#Override
public boolean isAmbiguous() {
return getBeans().size() > 1;
}
#Override
public void destroy(Object bean) {
if (bean instanceof DisposableBean) {
try {
((DisposableBean) bean).destroy();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
static class Bean {
private final boolean primary;
private final Object instance;
private final List<Annotation> annotations;
public Bean(Object instance, boolean primary) {
this.primary = primary;
this.instance = instance;
this.annotations = Arrays.asList(instance.getClass().getAnnotations());
}
public Object getInstance() {
return instance;
}
public boolean isPrimary() {
return primary;
}
public List<Annotation> getAnnotations() {
return annotations;
}
}
}
here is full source link: https://github.com/thomasdarimont/spring-boot-cdi-instance-example

can JBpm use Spring beans instead of classes?

We exploring migration of old J2EE project which has workflows kind of scenarios to be re-written with JBpm. I have referred few examples which mostly uses the Java class's for activity or task. Spring integration with JBPM is more of initialization of JBPM. Is it technically feasible to inject bean in substitution of java pojo classes?
It is possible to use spring bean in JBPM using domainSpecific work item handlers. Custom tasks can be defined with some required parameters such as bean name, method name, invocation parameters etc. These custom work item handler will implement ContextAware interface of Spring and get access to container.
This work in similar lines ServiceTask for JBPM which uses JAVA pojo classes.
<bean id="workflowLauncherProcessor" class="workflow.WorkflowLauncherProcessor">
<property name="manager" ref="runtimeManager"></property>
<property name="workItemHandlerMap">
<map>
<entry key="SpringAsyncTask" value-ref="asyncWorkItemHandler"></entry>
<entry key="SpringSyncTask" value-ref="syncWorkItemHandler"></entry>
<entry key="SpringDirectTask" value-ref="directWorkItemHandler"></entry>
</map>
</property>
</bean>
<bean id="directWorkItemHandler" class="workflow.handler.SpringDirectBeanInvokeWorkItemHandler">
</bean>
Implementation class will look as below
package workflow.handler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.management.InstanceNotFoundException;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jbpm.bpmn2.handler.WorkItemHandlerRuntimeException;
import org.kie.api.runtime.process.WorkItem;
import org.kie.api.runtime.process.WorkItemHandler;
import org.kie.api.runtime.process.WorkItemManager;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import com.scb.cashport.common.util.CommonConstants;
/**
* The Class SpringDirectBeanInvokeWorkItemHandler.
*
*/
public class SpringDirectBeanInvokeWorkItemHandler implements WorkItemHandler, ApplicationContextAware {
/** The Constant LOGGER. */
private static final Logger LOGGER = LogManager.getLogger(SpringDirectBeanInvokeWorkItemHandler.class);
/** The application context. */
private ApplicationContext applicationContext;
/*
* (non-Javadoc)
*
* #see
* org.kie.api.runtime.process.WorkItemHandler#executeWorkItem(org.kie.api
* .runtime.process.WorkItem, org.kie.api.runtime.process.WorkItemManager)
*/
#Override
public void executeWorkItem(WorkItem workItem, WorkItemManager manager) {
Map<String, Object> results = new HashMap<String, Object>();
try {
String bean = (String) workItem.getParameter(CommonConstants.BEAN);
Object inputParam = (Object) workItem.getParameter(CommonConstants.INPUT_PARAM);
boolean cascasdeInput = Boolean.valueOf(String.valueOf(workItem.getParameter(CommonConstants.CASCADE_INPUT)));
String operation = (String) workItem.getParameter(CommonConstants.OPERATION);
String parameterDefinition = (String) workItem.getParameter(CommonConstants.PARAMETER_TYPE);
String parameter = (String) workItem.getParameter(CommonConstants.PARAMETER);
LOGGER.info("Spring Direct Bean Work Item begin for bean: {} and input: {}", bean, inputParam);
Object outParam = null;
Object instance = applicationContext.getBean(bean);
if (instance != null) {
try {
Class<?>[] classes = null;
Object[] params = null;
if (parameterDefinition != null) {
if (parameterDefinition.contains(CommonConstants.COMMA)) {
String[] parameterDefinitionArray = parameterDefinition.split(CommonConstants.COMMA);
String[] parameternArray = parameter.split(CommonConstants.COMMA);
if (parameterDefinitionArray.length != parameternArray.length) {
throw new IllegalArgumentException("Paramter types and parameters are not matching");
}
classes = new Class<?>[parameterDefinitionArray.length];
params = new Object[parameterDefinitionArray.length];
for (int i = 0; i < parameterDefinitionArray.length; i++) {
classes[i] = Class.forName(parameterDefinitionArray[i]);
String paramKey = parameternArray[i];
if (paramKey.startsWith("'")) {
params[i] = paramKey.substring(1, (paramKey.length() - 1));
} else {
params[i] = BeanUtils.getProperty(inputParam, parameternArray[i]);
}
}
} else {
classes = new Class<?>[]{Class.forName(parameterDefinition)};
params = new Object[]{BeanUtils.getProperty(inputParam, parameter)};
}
}
Method method = instance.getClass().getMethod(operation, classes);
outParam = method.invoke(instance, params);
if (cascasdeInput) {
String resultParameter = (String) workItem.getParameter(CommonConstants.RESULT_PARAMETER);
BeanUtils.setProperty(inputParam, resultParameter, outParam);
results.put(CommonConstants.INPUT_PARAM, inputParam);
results.put(CommonConstants.OUT_PARAM, inputParam);
} else {
results.put(CommonConstants.OUT_PARAM, outParam);
}
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException cnfe) {
handleException(cnfe, instance.getClass().getName(), operation, parameterDefinition);
}
} else {
throw new InstanceNotFoundException(bean + " bean instance not found");
}
LOGGER.info("Spring Direct Bean Work Item completed for bean: {} and input: {}", bean, inputParam);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e.getCause());
throw new WorkItemHandlerRuntimeException(e);
} finally {
manager.completeWorkItem(workItem.getId(), results);
}
}
/*
* (non-Javadoc)
*
* #see
* org.kie.api.runtime.process.WorkItemHandler#abortWorkItem(org.kie.api
* .runtime.process.WorkItem, org.kie.api.runtime.process.WorkItemManager)
*/
#Override
public void abortWorkItem(WorkItem workItem, WorkItemManager manager) {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* #see
* org.springframework.context.ApplicationContextAware#setApplicationContext
* (org.springframework.context.ApplicationContext)
*/
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
Workflow launcher will be as below
RuntimeEngine engine = manager.getRuntimeEngine(EmptyContext.get());
KieSession ksession = engine.getKieSession();
for (Entry<String, WorkItemHandler> entry : workItemHandlerMap.entrySet()) {
ksession.getWorkItemManager().registerWorkItemHandler(entry.getKey(), entry.getValue());
}
Map<String, Object> parameter = new HashMap<String, Object>();
...
ProcessInstance processInstance = ksession.startProcess(processId, parameter);
Object variable = ((WorkflowProcessInstance) processInstance).getVariable("resultr");

Service not Autowired in JSF Converter [duplicate]

This question already has answers here:
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
(5 answers)
Closed 7 years ago.
I am working on a web application using PrimeFaces, JPA, Hibernate and JSF 2.0.
I have a converter for my JSF p:selectOneMenu. My problem is, when I run my application the Service descriptifService is not autowired, it return NULL !
The converter :
#Component
#FacesConverter(value = "descriptifConverter")
public class DescriptifConverter implements Converter {
#Autowired
#RmiClient
private IDescriptifService descriptifService;
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
if (arg2 == null || arg2.isEmpty()) {
return null;
}
String descriptif = arg2;
Long value = Long.valueOf(descriptif);
DescriptifDto result = new DescriptifDto();
result = descriptifService.findById(value);
return result;
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
if(arg2 == null || ((DescriptifDto) arg2).getIdDescriptif() == null) return null;
DescriptifDto descriptif = new DescriptifDto();
if(arg2 instanceof DescriptifDto) {
descriptif = (DescriptifDto) arg2;
String idDescriptif = descriptif.getIdDescriptif().toString();
return (idDescriptif != null) ? String.valueOf(idDescriptif) : null;
} else throw new ConverterException("Something wrong!" + arg2.hashCode() + arg2.toString());
}
}
The JSF code :
<p:selectOneMenu value="#{lotController.selectedDescriptif}"
effect="fade">
<f:selectItems value="#{lotController.listDescriptifs}" var="descriptif"
itemLabel="#{descriptif.libelle}" itemValue="#{descriptif}" />
<f:converter binding="#{descriptifConverter}" />
</p:selectOneMenu>
Here you have two options:
1 - Register a context provider bean:
AppContext Class:
import org.springframework.context.ApplicationContext;
public class AppContext {
private static ApplicationContext ctx;
public static void setApplicationContext(
ApplicationContext applicationContext) {
ctx = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
}
ApplicationContextProvider class:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class ApplicationContextProvider implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
AppContext.setApplicationContext(applicationContext);
}
}
Register the bean:
<bean id="contextApplicationContextProvider" class="com.example.ApplicationContextProvider" />
Now, through the context, you can get a reference to your service bean anyware:
IDescriptifService descriptifService = AppContext.getApplicationContext().getBean(
IDescriptifService.class);
2 - Store the converted values inside the ViewMap (inspired in this post)
I like this solution because it doesn't required database access which improves the performance of the application.
AbstractConverter class
import java.util.HashMap;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
public abstract class AbstractConverter implements Converter {
private static final String KEY = "com.example.converters.AbstractConverter";
protected Map<String, Object> getViewMap(FacesContext context) {
Map<String, Object> viewMap = context.getViewRoot().getViewMap();
#SuppressWarnings({ "unchecked", "rawtypes" })
Map<String, Object> idMap = (Map) viewMap.get(KEY);
if (idMap == null) {
idMap = new HashMap<String, Object>();
viewMap.put(KEY, idMap);
}
return idMap;
}
#Override
public final Object getAsObject(FacesContext context, UIComponent c,
String value) {
if (value == null || value.isEmpty()) {
return null;
}
return getViewMap(context).get(value);
}
#Override
public final String getAsString(FacesContext context, UIComponent c,
Object value) {
if (value != null) {
String id = getConversionId(value);
if (id == null || id.isEmpty()) {
throw new IllegalArgumentException(
"Objeto não pode ser convertido.");
}
getViewMap(context).put(id, value);
return id;
}
return null;
}
//Every concrete class must provide an unique conversionId String
//to every instance of the converted object
public abstract String getConversionId(Object value);
}
Here we create a storage place inside the ViewMap. We can now use it to store any converter object we need.
Here is an example of a concrete converter:
EntityConverter class
import javax.faces.convert.FacesConverter;
import com.example.AbstractEntity;
#FacesConverter("entity")
public class EntityConverter extends AbstractConverter {
#Override
public String getConversionId(Object value) {
if (value instanceof AbstractEntity) {
AbstractEntity entity = (AbstractEntity) value;
StringBuilder sb = new StringBuilder();
sb.append(entity.getClass().getSimpleName());
sb.append("#");
sb.append(entity.getId());
return sb.toString();
}
return null;
}
}
A late response, but I had the same problem of not being able to Autowire Spring beans into a JSF Converter, so I removed the #FacesConverter annotation and declared the converter component as session scoped, then, as you did, using the f:converter tag with binding attribute solved the problem. In your case:
#Component
#Scope(WebApplicationContext.SCOPE_SESSION)
public class DescriptifConverter implements Converter {
...
}
should work...

Resources