How to define a custom CamelBeanPostProcessor with Camel 2.9 - spring

Camel uses the CamelBeanPostProcessor class to support autowiring of camel endpoints, producer templates etc into beans using annotations.
The documentation for the class says:
If you use the element in your Spring XML then one of
these bean post processors is implicitly installed and configured for
you. So you should never have to explicitly create or configure one of
these instances.
but no mention on how to actually provide a custom implementation.
I use Spring and the <camelContext...> way of configuring camel. How do I provide my own implementation of CamelBeanPostProcessor, that way? The reason is I want to override the canPostProcessBean method to exclude a couple of bean types as post processing them causes a problem for us. They will never have any camel features in them, so it is not a problem.

I think the solution is to define your own BeanPostProcessor. As stated by the javadoc:
ApplicationContexts can autodetect BeanPostProcessor beans in their
bean definitions and apply them to any beans subsequently created.
Plain bean factories allow for programmatic registration of
post-processors, applying to all beans created through this factory.
So, if you use the camel xml like this:
<camel:beanPostProcessor/>
Just replace with:
<bean id="camel:beanPostProcessor"
class="org.mael.sample.camel.postprocessor.CustomCamelBeanPostProcessor" />
This way you will not use the CamelBeanPostProcessor registered by the xml, but your own BeanPostProcessor implementation.
But beware if you want to extend the CamelBeanPostProcessor class, I have seen the source and it uses an instance of org.apache.camel.impl.DefaultCamelBeanPostProcessor as a delegate and extend the later will cause some errors because the exceptions thrown do not match with the exceptions declared in the Spring interfaces.
The best option would be to use also a delegate in your own implementation and just override the methods you want i.e. canPostProcessBean
EDIT:
Here is something I did, check for the TODO comment for applying your functionality, also, pastebin:
package org.mike.sample.camel.postprocessor;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.annotation.XmlTransient;
import org.apache.camel.CamelContext;
import org.apache.camel.Endpoint;
import org.apache.camel.Service;
import org.apache.camel.core.xml.CamelJMXAgentDefinition;
import org.apache.camel.impl.CamelPostProcessorHelper;
import org.apache.camel.impl.DefaultCamelBeanPostProcessor;
import org.apache.camel.spring.GenericBeansException;
import org.apache.camel.util.ServiceHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class CustomCamelBeanPostProcessor implements BeanPostProcessor,
ApplicationContextAware {
private static final transient Logger LOG = LoggerFactory
.getLogger(CustomCamelBeanPostProcessor.class);
#XmlTransient
Set<String> prototypeBeans = new LinkedHashSet<String>();
#XmlTransient
private CamelContext camelContext;
#XmlTransient
private ApplicationContext applicationContext;
#XmlTransient
private String camelId;
// must use a delegate, as we cannot extend DefaultCamelBeanPostProcessor,
// as this will cause the
// XSD schema generator to include the DefaultCamelBeanPostProcessor as a
// type, which we do not want to
#XmlTransient
private final DefaultCamelBeanPostProcessor delegate = new DefaultCamelBeanPostProcessor() {
#Override
public CamelContext getOrLookupCamelContext() {
if (camelContext == null) {
if (camelId != null) {
LOG.trace(
"Looking up CamelContext by id: {} from Spring ApplicationContext: {}",
camelId, applicationContext);
camelContext = applicationContext.getBean(camelId,
CamelContext.class);
} else {
// lookup by type and grab the single CamelContext if exists
LOG.trace(
"Looking up CamelContext by type from Spring ApplicationContext: {}",
applicationContext);
Map<String, CamelContext> contexts = applicationContext
.getBeansOfType(CamelContext.class);
if (contexts != null && contexts.size() == 1) {
camelContext = contexts.values().iterator().next();
}
}
}
return camelContext;
}
#Override
public boolean canPostProcessBean(Object bean, String beanName) {
// the JMXAgent is a bit strange and causes Spring issues if we let
// it being
// post processed by this one. It does not need it anyway so we are
// good to go.
// We should also avoid to process the null object bean (in Spring
// 2.5.x)
// TODO - DO YOUR STUFF HERE STRELOK
if (bean == null || bean instanceof CamelJMXAgentDefinition) {
return false;
}
return super.canPostProcessBean(bean, beanName);
}
#Override
public CamelPostProcessorHelper getPostProcessorHelper() {
// lets lazily create the post processor
if (camelPostProcessorHelper == null) {
camelPostProcessorHelper = new CamelPostProcessorHelper() {
#Override
public CamelContext getCamelContext() {
// lets lazily lookup the camel context here
// as doing this will cause this context to be started
// immediately
// breaking the lifecycle ordering of different camel
// contexts
// so we only want to do this on demand
return delegate.getOrLookupCamelContext();
}
#Override
protected RuntimeException createProxyInstantiationRuntimeException(
Class<?> type, Endpoint endpoint, Exception e) {
return new BeanInstantiationException(type,
"Could not instantiate proxy of type "
+ type.getName() + " on endpoint "
+ endpoint, e);
}
protected boolean isSingleton(Object bean, String beanName) {
// no application context has been injected which means
// the bean
// has not been enlisted in Spring application context
if (applicationContext == null || beanName == null) {
return super.isSingleton(bean, beanName);
} else {
return applicationContext.isSingleton(beanName);
}
}
protected void startService(Service service, Object bean,
String beanName) throws Exception {
if (isSingleton(bean, beanName)) {
getCamelContext().addService(service);
} else {
// only start service and do not add it to
// CamelContext
ServiceHelper.startService(service);
if (prototypeBeans.add(beanName)) {
// do not spam the log with WARN so do this only
// once per bean name
CustomCamelBeanPostProcessor.LOG
.warn("The bean with id ["
+ beanName
+ "] is prototype scoped and cannot stop the injected service when bean is destroyed: "
+ service
+ ". You may want to stop the service manually from the bean.");
}
}
}
};
}
return camelPostProcessorHelper;
}
};
public CustomCamelBeanPostProcessor() {
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
try {
return delegate.postProcessBeforeInitialization(bean, beanName);
} catch (Exception e) {
// do not wrap already beans exceptions
if (e instanceof BeansException) {
throw (BeansException) e;
}
throw new GenericBeansException("Error post processing bean: "
+ beanName, e);
}
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
try {
return delegate.postProcessAfterInitialization(bean, beanName);
} catch (Exception e) {
// do not wrap already beans exceptions
if (e instanceof BeansException) {
throw (BeansException) e;
}
throw new GenericBeansException("Error post processing bean: "
+ beanName, e);
}
}
// Properties
// -------------------------------------------------------------------------
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
public CamelContext getCamelContext() {
return camelContext;
}
public void setCamelContext(CamelContext camelContext) {
this.camelContext = camelContext;
}
public String getCamelId() {
return camelId;
}
public void setCamelId(String camelId) {
this.camelId = camelId;
}
}

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.

Spring Autowired Shared Queue NullPointerException

I'm using Spring for the first time and am trying to implement a shared queue wherein a Kafka listener puts messages on the shared queue, and a ThreadManager that will eventually do something multithreaded with the items it takes off the shared queue. Here is my current implementation:
The Listener:
#Component
public class Listener {
#Autowired
private QueueConfig queueConfig;
private ExecutorService executorService;
private List<Future> futuresThread1 = new ArrayList<>();
public Listener() {
Properties appProps = new AppProperties().get();
this.executorService = Executors.newFixedThreadPool(Integer.parseInt(appProps.getProperty("listenerThreads")));
}
//TODO: how can I pass an approp into this annotation?
#KafkaListener(id = "id0", topics = "bose.cdp.ingest.marge.boseaccount.normalized")
public void listener(ConsumerRecord<?, ?> record) throws InterruptedException, ExecutionException
{
futuresThread1.add(executorService.submit(new Runnable() {
#Override public void run() {
try{
queueConfig.blockingQueue().put(record);
// System.out.println(queueConfig.blockingQueue().take());
} catch (Exception e){
System.out.print(e.toString());
}
}
}));
}
}
The Queue:
#Configuration
public class QueueConfig {
private Properties appProps = new AppProperties().get();
#Bean
public BlockingQueue<ConsumerRecord> blockingQueue() {
return new ArrayBlockingQueue<>(
Integer.parseInt(appProps.getProperty("blockingQueueSize"))
);
}
}
The ThreadManager:
#Component
public class ThreadManager {
#Autowired
private QueueConfig queueConfig;
private int threads;
public ThreadManager() {
Properties appProps = new AppProperties().get();
this.threads = Integer.parseInt(appProps.getProperty("threadManagerThreads"));
}
public void run() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(threads);
try {
while (true){
queueConfig.blockingQueue().take();
}
} catch (Exception e){
System.out.print(e.toString());
executorService.shutdownNow();
executorService.awaitTermination(1, TimeUnit.SECONDS);
}
}
}
Lastly, the main thread where everything is started from:
#SpringBootApplication
public class SourceAccountListenerApp {
public static void main(String[] args) {
SpringApplication.run(SourceAccountListenerApp.class, args);
ThreadManager threadManager = new ThreadManager();
try{
threadManager.run();
} catch (Exception e) {
System.out.println(e.toString());
}
}
}
The problem
I can tell when running this in the debugger that the Listener is adding things to the queue. When the ThreadManager takes off the shared queue, it tells me the queue is null and I get an NPE. It seems like autowiring isn't working to connect the queue the listener is using to the ThreadManager. Any help appreciated.
This is the problem:
ThreadManager threadManager = new ThreadManager();
Since you are creating the instance manually, you cannot use the DI provided by Spring.
One simple solution is implement a CommandLineRunner, that will be executed after the complete SourceAccountListenerApp initialization:
#SpringBootApplication
public class SourceAccountListenerApp {
public static void main(String[] args) {
SpringApplication.run(SourceAccountListenerApp.class, args);
}
// Create the CommandLineRunner Bean and inject ThreadManager
#Bean
CommandLineRunner runner(ThreadManager manager){
return args -> {
manager.run();
};
}
}
You use SpringĀ“s programatic, so called 'JavaConfig', way of setting up Spring beans (classes annotated with #Configuration with methods annotated with #Bean). Usually at application startup Spring will call those #Bean methods under the hood and register them in it's application context (if scope is singleton - the default - this will happen only once!). No need to call those #Bean methods anywhere in your code directly... you must not, otherwise you will get a separate, fresh instance that possibly is not fully configured!
Instead, you need to inject the BlockingQueue<ConsumerRecord> that you 'configured' in your QueueConfig.blockingQueue() method into your ThreadManager. Since the queue seems to be a mandatory dependency for the ThreadManager to work, I'd let Spring inject it via constructor:
#Component
public class ThreadManager {
private int threads;
// add instance var for queue...
private BlockingQueue<ConsumerRecord> blockingQueue;
// you could add #Autowired annotation to BlockingQueue param,
// but I believe it's not mandatory...
public ThreadManager(BlockingQueue<ConsumerRecord> blockingQueue) {
Properties appProps = new AppProperties().get();
this.threads = Integer.parseInt(appProps.getProperty("threadManagerThreads"));
this.blockingQueue = blockingQueue;
}
public void run() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(threads);
try {
while (true){
this.blockingQueue.take();
}
} catch (Exception e){
System.out.print(e.toString());
executorService.shutdownNow();
executorService.awaitTermination(1, TimeUnit.SECONDS);
}
}
}
Just to clarify one more thing: by default the method name of a #Bean method is used by Spring to assign this bean a unique ID (method name == bean id). So your method is called blockingQueue, means your BlockingQueue<ConsumerRecord> instance will also be registered with id blockingQueue in application context. The new constructor parameter is also named blockingQueue and it's type matches BlockingQueue<ConsumerRecord>. Simplified, that's one way Spring looks up and injects/wires dependencies.

Spring #Autowired annotated object value is null

// My Factory class
#Component
public class UserRewardAccountValidatorFactory {
#Autowired
private VirginAmericaValidator virginAmericaValidator;
private static class SingletonHolder {
static UserRewardAccountValidatorFactory instance = new UserRewardAccountValidatorFactory();
}
public static UserRewardAccountValidatorFactory getInstance() {
return SingletonHolder.instance;
}
private UserRewardAccountValidatorFactory() {}
public PartnerValidator getPartnerValidator(Partner partner){
return virginAmericaValidator;
}
}
// My Validator class
#Service
public class VirginAmericaValidator implements PartnerValidator {
#Override
public void validate(String code) throws InvalidCodeException{
//do some processing if processing fails throw exception
if (code.equals("bad".toString())){
throw new InvalidCodeException();
}
}
}
//Usage:
PartnerValidator pv = UserRewardAccountValidatorFactory.getInstance().getPartnerValidator(partner);
if (pv != null){
try {
pv.validate(userRewardAccount);
} catch (InvalidCodeException e){
return buildResponse(ResponseStatus.INVALID_USER_REWARD_ACCOUNT, e.getMessage());
}
}
My package scan level is at much higher level. Whats happening is my virginAmericaValidator is always empty. Why is #Autowired annotation not working.
Your current approach will not work with Spring as you are ultimately using new UserRewardAccountValidatorFactory to create the instance which essentially bypasses Spring context altogether. Two approaches that should possibly work are these:
a. Using a factory-method and using xml to define your bean:
<bean class="package.UserRewardAccountValidatorFactory" name="myfactory" factory-method="getInstance"/>
This will essentially return the instance that you are creating back as a Spring bean and should get autowired cleanly.
b. Using Java #Configuration based mechanism:
#Configuration
public class MyBeanConfiguration {
#Bean
public UserRewardAccountValidatorFactory myFactory() {
return UserRewardAccountValidatorFactory.getInstance();
}
}

What is the Spring DI equivalent of CDI's InjectionPoint?

I would like to create a Spring's bean producer method which is aware who invoked it, so I've started with the following code:
#Configuration
public class LoggerProvider {
#Bean
#Scope("prototype")
public Logger produceLogger() {
// get known WHAT bean/component invoked this producer
Class<?> clazz = ...
return LoggerFactory.getLogger(clazz);
}
}
How can I get the information who wants to get the bean injected?
I'm looking for some equivalent of CDI's InjectionPoint in Spring world.
Spring 4.3.0 enables InjectionPoint and DependencyDescriptor parameters for bean producing methods:
#Configuration
public class LoggerProvider {
#Bean
#Scope("prototype")
public Logger produceLogger(InjectionPoint injectionPoint) {
Class<?> clazz = injectionPoint.getMember().getDeclaringClass();
return LoggerFactory.getLogger(clazz);
}
}
By the way, the issue for this feature SPR-14033 links to a comment on a blog post which links to this question.
As far as I know, Spring does not have such a concept.
Then only thing that is aware of the point that is processed is a BeanPostProcessor.
Example:
#Target(PARAMETER)
#Retention(RUNTIME)
#Documented
public #interface Logger {}
public class LoggerInjectBeanPostProcessor implements BeanPostProcessor {
public Logger produceLogger() {
// get known WHAT bean/component invoked this producer
Class<?> clazz = ...
return LoggerFactory.getLogger(clazz);
}
#Override
public Object postProcessBeforeInitialization(final Object bean,
final String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(final Object bean,
final String beanName) throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(),
new FieldCallback() {
#Override
public void doWith(final Field field) throws IllegalArgumentException, IllegalAccessException {
field.set(bean, produceLogger());
}
},
new ReflectionUtils.FieldFilter() {
#Override
public boolean matches(final Field field) {
return field.getAnnotation(Logger.class) != null;
}
});
return bean;
}
}

Create a Pool of JAXB Unmarshaller

I was looking around to find a way to improve JAXB Unmarshalling performances processing huge sets of files and found the following advice:
"If you really care about the performance, and/or your application is going to read a lot of small documents, then creating Unmarshaller could be relatively an expensive operation. In that case, consider pooling Unmarshaller objects"
Googling the web to find an example of this didn't return anything, so I thought it may be of interest to put my implementation here using Spring 3.0 and Apache Commons Pool.
UnmarshallerFactory.java
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import org.apache.commons.pool.KeyedPoolableObjectFactory;
import org.springframework.stereotype.Component;
/**
* Pool of JAXB Unmarshallers.
*
*/
#Component
public class UnmarshallerFactory implements KeyedPoolableObjectFactory {
// Map of JAXB Contexts
#SuppressWarnings("rawtypes")
private final static Map<Object, JAXBContext> JAXB_CONTEXT_MAP = new HashMap<Object, JAXBContext>();
#Override
public void activateObject(final Object arg0, final Object arg1) throws Exception {
}
#Override
public void passivateObject(final Object arg0, final Object arg1) throws Exception {
}
#Override
public final void destroyObject(final Object key, final Object object) throws Exception {
}
/**
* Create a new instance of Unmarshaller if none exists for the specified
* key.
*
* #param unmarshallerKey
* : Class used to create an instance of Unmarshaller
*/
#SuppressWarnings("rawtypes")
#Override
public final Object makeObject(final Object unmarshallerKey) {
if (unmarshallerKey instanceof Class) {
Class clazz = (Class) unmarshallerKey;
// Retrieve or create a JACBContext for this key
JAXBContext jc = JAXB_CONTEXT_MAP.get(unmarshallerKey);
if (jc == null) {
try {
jc = JAXBContext.newInstance(clazz);
// JAXB Context is threadsafe, it can be reused, so let's store it for later
JAXB_CONTEXT_MAP.put(unmarshallerKey, jc);
} catch (JAXBException e) {
// Deal with that error here
return null;
}
}
try {
return jc.createUnmarshaller();
} catch (JAXBException e) {
// Deal with that error here
}
}
return null;
}
#Override
public final boolean validateObject(final Object key, final Object object) {
return true;
}
}
UnmarshallerPool.java
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class UnmarshallerPool extends GenericKeyedObjectPool {
#Autowired
public UnmarshallerPool(final UnmarshallerFactory unmarshallerFactory) {
// Make usage of the factory created above
super(unmarshallerFactory);
// You'd better set the properties from a file here
this.setMaxIdle(4);
this.setMaxActive(5);
this.setMinEvictableIdleTimeMillis(30000);
this.setTestOnBorrow(false);
this.setMaxWait(1000);
}
public UnmarshallerPool(UnmarshallerFactory objFactory,
GenericKeyedObjectPool.Config config) {
super(objFactory, config);
}
#Override
public Object borrowObject(Object key) throws Exception {
return super.borrowObject(key);
}
#Override
public void returnObject(Object key, Object obj) throws Exception {
super.returnObject(key, obj);
}
}
And in your class that require a JAXB Unmarshaller:
// Autowiring of the Pool
#Resource(name = "unmarshallerPool")
private UnmarshallerPool unmarshallerPool;
public void myMethod() {
Unmarshaller u = null;
try {
// Borrow an Unmarshaller from the pool
u = (Unmarshaller) this.unmarshallerPool.borrowObject(MyJAXBClass.class);
MyJAXBClass myJAXBObject = (MyJAXBClass) u.unmarshal(url);
// Do whatever
} catch (Exception e) {
// Deal with that error
} finally {
try {
// Return the Unmarshaller to the pool
this.unmarshallerPool.returnObject(MyJAXBClass.class, u);
} catch (Exception ignore) {
}
}
}
This example is naive as it uses only one Class to create the JAXBContext and uses the same Class instance as the Key for the Keyed Pool. This can be improved by passing an Array of Classes as parameter rather than only one Class.
Hope this can help.
The creation of unmarshallers is intended to be light. I would recommend doing some profiling before developing a pooling strategy.

Resources