How to register JSF ExceptionHandlerFactory programmatically in Spring Boot - spring-boot

I'm using Joinfaces to build a JSF + Spring Boot application and Omnifaces is packed with it.
When the View expires and I navigate I get the ViewExpiredException. When I execute Ajax, the page does nothing and the error shows in the console.
Is it possible to register the org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory programatically with Spring, without having to add a .xml (web, faces-config) to my project?

Use the following to set up a custom exception handler sans web.xml:
FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,"org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory");
The trick here is to make sure this line is executed as early in the startup as possible; once FactoryFinder.getFactory() has been called by the JSF runtime, it's too late to change the configured handler.
The good thing is that I actually can't find anywhere in the Mojarra codebase where the exception handler factory is being set by default, so you probably could execute this maybe in the constructor (not the #PostConstructor) of any #ApplicationScoped bean. You could also do it in a static initializer of the ame bean.
Additionally, you could do it in a FacesInitializer. So asssuming you're running Mojarra, you'll need to setup the handler very early on in the startup process of the servlet context
public class YourWebAppInitializer extends FacesInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext ctxt) throws ServletException {
AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
root.register(YourSpringConfigClass.class);
ctxt.addListener(new ContextLoaderListener(root));
FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,"org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory");
}
}
The WebApplicationInitializer is a standard interface supported by Spring for bootstrapping a web application and I'm assuming you already have that in place because you don't have a web.xml - feel free to replace the contents of the onStartup method with whatever you have in your actual implementation. The key thing here is to make sure you set the factory there, which is pretty early in the startup of the application.
Also note that you can hand set the actual ExceptionHandler on any given instance of FacesContext (although I haven't tested this to see how it'll behave or whether it'll perform well)

Related

Using both WebApplicationInitializer and web.xml in spring mvc+spring security+spring session redis web application

I'm trying to implement Spring redis session in an existing Spring MVC (ver 5.1.6) application. In web.xml we have ContextLoaderListener, DispatcherServlet and contextConfigLocation are all defined.
After required dependencies are included and suggested code changes are done, i'm getting below error:
Caused by: java.lang.IllegalStateException: Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader definitions in your web.xml!"}}*
As part of code changes i'm extending the class "AbstractHttpSessionApplicationInitializer",(from Spring session core library) which internally implements WebApplicationInitializer. Seems like that is trying to create another context and throwing the above error. We cannot avoid extending this class, as this does the job of registering redisHttpSession to context.
Most of the examples available are all with spring boot. So there they wouldn't have faced this issue.
Any solution, other than completely replacing web.xml and use only WebApplicationInitializer?
Just want to provide an update. Instead of extending AbstractHttpSessionApplicationInitializer abtract class, i have taken a different approach by initializing bean RedisHttpSessionConfiguration thru XML bean definition.
This approach worked.
Followed the steps mentioned in the below thread;
How to configure Spring sessions to work with Redis in xml?
Along with that we need to serialize the cookie as well;
#Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("SESSIONID");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}

Spring Framework 5.0.0.Final parent context not getting loaded

I was trying with latest spring 5.0.0.Final with my EAR project which has a parent context defined in web.xml using context-param
with param names locatorFactorySelector and parentContextKey but spring could not able to load the parent context. When i checked the ContextLoaderListener source code it seems like there is no logic applied to pick parent context. Here my question is does spring 5 provide any default implementation of ContextLoader which caters loading of parent context or spring 5 dropped, if not what is the approach to support this, do i have to implement our own ?
The loading of the parent context based on locatorFactorySelector were handled at ContextLoader#loadParentContext(). But they changed it to return null in this commit.
As said by the javadoc , I think you can create a new ContextLoaderListener and override this method to return the parent context:
public class FooContextLoaderListener extends ContextLoaderListener{
#Override
protected ApplicationContext loadParentContext(ServletContext servletContext) {
//load and return the parent context ......
}
}
Then use this ContextLoaderListener to start up Spring :
<listener>
<listener-class>org.foo.bar.FooContextLoaderListener</listener-class>
</listener>
For me this below piece of code worked fine.
public class BeanFactoryContextLoaderListener extends ContextLoaderListener {
private static Logger log = Logger.getLogger(BeanFactoryContextLoaderListener.class);
#Override
protected ApplicationContext loadParentContext(ServletContext servletContext) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beanRefFactory.xml");
return ctx;
}
}
Obviously I added a listener too in web.xml.
My team recently bumped into the same problem. We wanted to start using Webflux and it requires Spring 5.
Here is what I did:
Manually reintroduce BeanFactoryLocator mechanism. Take following classes from Spring 4, put it into your code and fix packages:
AbstractUrlMethodNameResolver
AnnotationMethodHandlerAdapter
BeanFactoryLocator
BeanFactoryReference
BootstrapException
ContextSingletonBeanFactoryLocator
DefaultAnnotationHandlerMapping
HandlerMethodInvocationException
HandlerMethodInvoker
HandlerMethodResolver
InternalPathMethodNameResolver
MethodNameResolver
NoSuchRequestHandlingMethodException
ServletAnnotationMappingUtils
SingletonBeanFactoryLocator
SourceHttpMessageConverter
WebUtils
XmlAwareFormHttpMessageConverter
Following Subhranil's advice from this thread, use custom ContextLoaderListener which loads parent context same as in Spring 4. Then use it in web.xml.
In each WAR's spring-servlet.xml add DefaultAnnotationHandlerMapping so it scans for controllers. Accompanying beans like AnnotationMethodHandlerAdapter are also needed.
It worked for us.
If all you need is your context-param in any of your spring managed class you are looking for ServletContextAware.
Just implement that class and override its method to get the ServletContext object. Later you can also get the context-params using the ServletContext object.
Check out a very similar question.
Apparently, the mechanism for locating the parent context was removed with SPR-15154 (see also the corresponding Github issue spring-framework#19720).
One workaround is to extend org.springframework.web.context.ContextLoaderListener and re-implement the loadParentContext method is described in this stackoverflow answer.
There is probably a better way to solve parent context loading in Spring 5.x, which I still have to figure out.

Spring adding beans at runtime

I'm trying to come up with a way to add spring beans dynamically after an application has started.
I've found a couple of places with similar questions such as in here
And I'm aware of ApplicationContext extension points such as ApplicationContext events and BeanFactoryPostProcessor.
The issue I have at hand is that I need to add beans after some beans have been created, I guess that discards the BeanFactoryPostProcessor option, as it would happen before the application context starts registering beans.
I tried to add a singletonBean after the context was refreshed:
#EventListener
public void postProcess(ContextRefreshedEvent refreshedEvent) throws BeansException {
ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext)refreshedEvent.getApplicationContext()).getBeanFactory();
List<Api> apis = repository.findAll();
apis.forEach(api -> {
api.getEndpoints().forEach(endpoint -> {
HttpRequestHandlingMessagingGateway gateway = createGateway(endpoint);
customIntegrationHandlerMapping.register(gateway);
beanFactory.registerSingleton("httpflow-"+endpoint.getId(),createEndpointFlow(gateway));
});
});
}
The problem is that IntegrationFlow depends on a post processor that is not triggered after registering the singleton bean. I can't really force a refresh here.
Is there a way out of this chicken-egg problem?
See AutowireCapableBeanFactory.initializeBean(beanName).
You need to make sure the bean's not used between registration and initialization.
Also, be aware that registering singletons after the context is initialized wasn't really thread-safe until recently (4.2.2, I think). It could cause ConcurrentModificationExceptions if other code is iterating over the beans in the factory.
However, in this case, it might be too late to get the HTTP paths registered, you might need more code to do that.

How can I instantiate an injected class (using Spring) before logback configuration

I am using spring to inject a class into my PropertyDefiner implementation which will be used to help set up some properties within the logback.xml file (through dynamic property loading).
I'd love to get this class loaded and instantiated before logback is configured. Any thoughts on how to do this?
If you're using annotations in Spring, it's convenient to do this by marking the class (i.e. the dependency) you'll be injecting as #Component and then using #Autowired in your PropertyDefiner implementation. This ensures that the first class will be instantiated first. http://static.springsource.org/spring/docs/3.0.0.M3/spring-framework-reference/html/ch04s12.html
Any other initialization you require could be achieved using instance initializer blocks http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
I do not know if this can be done elegantly at present time (2012-07). However, support for injection has been requested in LOGBACK-719.
If your bean factory implements AutowireCapableBeanFactory, given the Spring Applicaton context, you could invoke autowireBean(Object existingBean) to autowire the bean. Here is a tentative implementation:
class Your.PropertyDefiner implements PropertyDefiner, LifeCycle {
#Autowired
#Qualifier("myKey")
String myKey;
public void start() {
ApplicationContext appContext = ... somehow get the spring app context
AutowireCapableBeanFactory factory = appContext.getAutowireCapableBeanFactory();
factory.autowireBean(this); // declare victory
}
}
The start() method will be invoked only if your PropertyDefiner implements the LifeCycle interface. Moreover, you need logback version 1.0.7 or later. Earlier versions do not invoke start().
My solution resulted in not implementing a PropertyDefiner. The original question became an issue of not having the application context from spring to set the dynamic properties. I'm not sure why, but code in a later listener (after the Spring listeners) would get called (invoking the LoggerFactory call) before the application context was available. I tried a number of things, until I starting looking at a different approach.
Instead of using dynamic properties I created a listener (called on server startup) which then programmatically sets up my appender with the properties I want (through the createAdminNotifyAppender).
#Override
public void contextInitialized(ServletContextEvent arg0)
{
//Set up the property reader to pull the correct properties
ServletContext context = arg0.getServletContext();
ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(context);
propReader = (AppConfigPropertiesReader)appContext.getBean("propertySourcesPlaceholder");
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
createAdminNotifyAppender(lc, propReader);
}
The createAdminNotify method simply sets up an appender and adds it to the logging context. (if you're really interested, you can see that method's implementation on this thread).
Now I have a separate and modular listener that I can add to other apps that are using logback, but possibly with different properties. The properties are pulled from a database and can also vary by environment.

Spring: how AnnotationConfigWebApplicationContext could not overwrite later beans?

I have a web application that use Sring IoC framework.
I use the Java configuration for Spring, and I only use #Configuration annoted module definition (no DI related tags elsewhere in the code).
The Spring registry is built on web application start-up thanks to (a bit modified version of) Spring context loader listener, and the contextConfigLocation param in web.xml configured to point to the #Configuration annotated class.
All that is good and I get a AnnotationConfigWebApplicationContext.
Now, I want to have plugins in my application, that will have their own #Configuration annotated configuration classes, and will use some of the main application services. BUT I don't want to have main application to be modified to load these new modules.
So, I thought that I could simply use the package searching of annotated class for that, but now, it seems that I can use two beans with the same type, even if they have different ids, and clearly AnnotationConfigWebApplicationContext doc states that:
Note: In case of multiple #Configuration classes, later #Bean definitions will override ones defined in earlier loaded files. This can be leveraged to deliberately override certain bean definitions via an extra Configuration class.
I don't want that, because modules should be able to contribute alternative version of services, not (alwways) override existing one - especcially if I want to have a "moduleDef" bean.
I tried to use differents approach on that, but the hierachy of Context and related services is just to big for me.
So, does anybody know how I could reach my goal ?
Thanks
You can have multiple beans of the same type, but You cannot have 2 or more beans with the same ID in a single Spring ApplicationContext - no matter if You use XML or JavaConfig.
The overriding mechanism matches the bean ID's, so all You need to do is to ensure unique ID, i.e.: coreModuleDef, someOtherModuleDef, anotherModuleDef. I don't think You need the ID of each module definition to be identical? What should be sufficient is the type to be the same, but not ID.
You can also turn off the overriding mechanism by setting allowBeanDefinitionOverriding to false on Your AnnotationConfigWebApplicationContext to get an exception if You accidentally override a bean:
public class MyDispatcherServlet extends DispatcherServlet {
#Override
protected void postProcessWebApplicationContext(
ConfigurableWebApplicationContext wac) {
((AnnotationConfigWebApplicationContext) wac)
.setAllowBeanDefinitionOverriding(false);
}
}
or:
public class MyContextLoaderListener extends ContextLoaderListener {
#Override
protected void customizeContext(
ServletContext servletContext,
ConfigurableWebApplicationContext applicationContext) {
((AnnotationConfigWebApplicationContext) wac)
.setAllowBeanDefinitionOverriding(false);
}
}

Resources