Adding Spring bean asynchronously - spring

In my Grails 3.1.14 I app need to add a bean, created in a callback of the asynchronous method call:
Vertx.clusteredVertx( [:] ){ AsyncResult<Vertx> res ->
if( res.succeeded() ){
Vertx vertx = res.result() // << this should be injected into the appContext
}
}
so that the instance can be autowired into other artefacts across the whole application.
What's the proper way to achieve this?
Shall I do it with StaticApplicationContext or would it break anything?
Another way would be to use a "container-bean" and set it's propery upon completion of the async method, but it's kind of ugly.

You can create a wrapper bean that configures its properties based on application context events, using InitializingBean interface.
class VertxWrapper implements InitializingBean {
Vertx vertx
void afterPropertiesSet() throws Exception {
Vertx.clusteredVertx( [:] ){ AsyncResult<Vertx> res ->
this.vertx = res.result() // << this should be injected into
}
}
}
}
And then simply access vertxWrapper.vertx from any bean that has vertxWrapper injected into it.

Related

Do JASON internal actions work with Spring Autowire?

I am developing an application using JADE, JASON (Agent frameworks) and Spring Boot. Basically what I have is a JADE Container where Both Jade and Jason Agents are registered in. And Since I am using Spring, I tend to Autowire services. In that case I am in need to access some services, inside some of my Jason internal actions (which I custom wrote extending DefaultInternalAction class). which seems not working. I have the idea how to Autowire and how the Beans work. My doubt is whether those internal actions are in the spring context or not. I guess they are not. Thats why may be the Autowire thing is not working. Can someone please explain me about the real action inside the jade container and internal actions so that I can think differently about using Autowire inside jason internal actions.
As far as I know, internal actions is created by jason, not spring that is why you cant autowire services. Personnaly, I create factory and use it for getting instance of a service. Something like this:
public class SpringPluginFactory {
private static final SpringPluginFactory INSTANCE = new SpringPluginFactory();
private ApplicationContext applicationContext;
private SpringPluginFactory(){}
private <T> T createPlugin(Class<T> iface) {
if(applicationContext == null){
throw new IllegalStateException("applicationContext cannot be null");
}
try {
return applicationContext.getBean(iface);
} catch (Exception e) {
throw new RuntimeException("factory unable to construct instance of " + iface.getName());
}
}
public static <T> T getPlugin(Class<T> iface){
return INSTANCE.createPlugin(iface);
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
then I create bean in order to set aplicationContext:
#Bean
public SpringPluginFactory pluginFactory(ApplicationContext applicationContext){
SpringPluginFactory pluginFactory = SpringPluginFactory.INSTANCE;
pluginFactory.setApplicationContext(applicationContext);
return pluginFactory;
}
and use the factory in any behaviours or internal actions
SpringPluginFactory.getPlugin(YouService.class).doSomething();
Maybe it will help.

How to define the execution order of interceptor in Spring Boot application?

I define an interceptor and register it in a class (annotated with Configuration) which extends WebMvcConfigurerAdapter; however, I also use some third-party libraries which also define some interceptors. I want my interceptor to be the last one in the interceptor execution chain. It seems there is no way to enforce this. How to define the execution order of interceptor in Spring Boot application?
If we've Multiple Interceptors, Instead of #Order Annotation we can do as below.
#EnableWebMvc
#Configuration
public class WebMVCConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry
.addWebRequestInterceptor(new WebRequestInterceptor() {
//Overrides
}).order(Ordered.HIGHEST_PRECEDENCE);
registry
.addWebRequestInterceptor(new WebRequestInterceptor() {
//Overrides
}).order(Ordered.LOWEST_PRECEDENCE);
}
}
All Feign clients become proxies, so there is only one way to change the order of the request interceptors. But you cannot use it, because if you change the proxy to SGLIB, it will not work.
if(bean instanceof YoursFeignClientBean) {
Class<Proxy> superclass = (Class<Proxy>) bean.getClass().getSuperclass();
Field h = superclass.getDeclaredField("h");
h.setAccessible(true);
// its FeignInvocationHandler
InvocationHandler ih = (InvocationHandler) ReflectionUtils.getField(h, bean);
Field dispatch = ih.getClass().getDeclaredField("dispatch");
dispatch.setAccessible(true);
Map<Method, InvocationHandlerFactory.MethodHandler> map =
(Map<Method, InvocationHandlerFactory.MethodHandler>) dispatch.get(ih);
for (Method method : map.keySet()) {
InvocationHandlerFactory.MethodHandler handler = map.get(method);
Field requestInterceptors = handler.getClass().getDeclaredField("requestInterceptors");
requestInterceptors.setAccessible(true);
List<RequestInterceptor> interceptorList = (List<RequestInterceptor>)
ReflectionUtils.getField(requestInterceptors, handler);
RequestInterceptor ri = interceptorList.stream().filter(t -> t.getClass().getName().startsWith(YoursFeignConfig.class.getName())).findFirst().get();
// reorder
interceptorList.remove(ri);
interceptorList.add(ri);
}
According to my experience, Interceptors are adding a stack. For this reason you should addRegister above which one you want before calling than other.

Spring get bean only if already instanced for scope

Hi i'm using Spring 3
i'm using
applicationContext.getBeansOfType
is there a possibility to get only the beans already instanciated for the current scope?
I don't want to instanciate Beans which have not already been used in the current request.
I think that not, but you could write one, something like:
public static List<Object> getBeansInScope(Class<?> type, AbstractApplicationContext ctx, int scope) {
List<Object> beans = new ArrayList<Object>();
String[] names = ctx.getBeanNamesForType(type);
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
for (String name : names) {
Object bean = attributes.getAttribute(name, scope);
if (bean != null)
beans.add(bean);
}
return beans;
}
That only work for request and session scopes.

EJB 3.0 CMP Transaction after a rollback

I'm having some trouble with transaction management in EJB3.0. What I want to do is to log an error into the database in case an exception happens.
For that purpose I have 2 stateless beans: Bean A and Bean B.
Bean A does the following:
save something
call Bean B to log an error if needed
In Step 1, the save is basically using the EntityManager#merge(-) method.
In Step 2, I have put the following lines at the top of Bean B:
#Stateless(name = "ErrorLogDAO", mappedName = "ErrorLogDAO")
#Remote
#Local
#TransactionManagement(TransactionManagementType.CONTAINER)
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class ErrorLogDAOBean {...}
However, when an exception is taking place in the save method, I'm catching it and then I manually invoke the ctx.setRollBackOnly() method and after that I call ErrorLogDAOBean that inserts an error log to the DB. But the error log is not being inserted and the error I'm getting is:
javax.transaction.TransactionRolledbackException: EJB Exception: :
weblogic.transaction.internal.AppSetRollbackOnlyException at
weblogic.transaction.internal.TransactionImpl.setRollbackOnly(TransactionImpl.java:551)
at
weblogic.transaction.internal.TransactionManagerImpl.setRollbackOnly(TransactionManagerImpl.java:319)
at
weblogic.transaction.internal.TransactionManagerImpl.setRollbackOnly(TransactionManagerImpl.java:312)
at
org.eclipse.persistence.transaction.JTATransactionController.markTransactionForRollback_impl(JTATransactionController.java:145)
at
org.eclipse.persistence.transaction.AbstractTransactionController.markTransactionForRollback(AbstractTransactionController.java:196)
at
org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.rollbackTransaction(UnitOfWorkImpl.java:4486)
at
org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1351)
at
org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:468)
at
org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithPreBuiltChangeSet(UnitOfWorkImpl.java:1439)
at
org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.writeChanges(RepeatableWriteUnitOfWork.java:316)
at
org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:527)
....
I'm well familiar with transaction management logic, and based on the code above I assumed I had this covered, but it appears not.
Any ideas?
UPDATE
Bean A Code:
#TransactionManagement(value = TransactionManagementType.CONTAINER)
#TransactionAttribute(value = REQUIRED)
public class WMServiceBOBean {
public void saveInBeanA {
int errorCode = save();
if (errorCode != SUCCESS)
{
ClassX.logError();
ctx.setRollbackOnly();
return errorCode;
}
}
}
Class X Code:
public class classX
{
...
public void logError()
{
ErrorLog e = new ErrorLog;
BeanB beanB = //Local lookup of Bean B
beanB.insertErrorLog (e);
}
...
}
BEAN B Code:
#TransactionManagement(TransactionManagementType.CONTAINER)
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class ErrorLogDAOBean
{
...
public void insertErrorLog (ErrorLog e)
{
merge (e);
}
...
}
I finally figured out the problem.
Here's the problem, when looking up the ErrorLogBean, i was instantiating a new Persistence Manager. When the transaction gets flagged for rollback, the process of getting a new PM was failing. I know it doesn't make sense to get a new Persistence Manager but it was part of test we are conducting.
Thanks Piotr for all your help in this!

Get all existing session beans from all users in Spring

Is there a way to get all existing session beans managed by Spring at runtime? Getting them for the current user is easy.
Any suggestions?
Thanks,
XLR
I don't do Spring, but in normal JSF/JSP/Servlet you would grab HttpSessionBindingListener for this. Basically you need to give the session scoped bean a static List<Bean> property and implement the interface accordingly that it updates the static list in the valueBound() and valueUnbound() methods.
You can find a detailed code example in this answer.
Here is a solution I came up with that utilizes Spring:
I make a normal Spring singleton bean called SessionBeanHolder.
This bean holds a list of my session beans.
When a user logs in, I add the session bean to my SessionBeanHolder.
When referring to session beans in Spring, you are actually referring to proxies.
So the key thing to making this work was to fetch the underlying bean to add to the SessionBeanHolder.
Below is the sample code:
Note: My session bean is called SessionInfo.
#Scope(value="singleton")
#Component
public class SessionBeanHolder {
static Set<SessionInfo> beans;
public SessionBeanHolder() {
beans = new HashSet<SessionInfo>();
}
public Collection<SessionInfo> getBeans() {
return beans;
}
public void addBean(SessionInfo bean) {
try {
this.beans.add(removeProxyFromBean(bean));
} catch (Exception e) {
e.printStackTrace();
}
}
// Fetch the underlying bean that the proxy refers to
private SessionInfo removeProxyFromBean(SessionInfo proxiedBean) {
if (proxiedBean instanceof Advised) {
try {
return (SessionInfo) ((Advised) proxiedBean).getTargetSource().getTarget();
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
return proxiedBean;
}
}
}
Naturally, whenever you want to add session bean or fetch a list of all beans, just autowire the SessionBeanHolder and use its methods.
#Autowired
SessionBeanHolder sessionBeanHolder;

Resources